【三】C++引用机制
2015-08-14 16:52
316 查看
目录
目录1变量名回顾
2引用的概念
3引用的意义
4const引用
5引用的本质
6函数返回引用
7C对三目运算符做了什么
8小结
1、变量名回顾
变量是一段实际连续存储空间的别名程序中通过变量来申请并命名存储空间
通过变量的名字可以使用存储空间
如图:
思考:
对于一段连续的存储空间只能有一个别名吗?
肯定不是,因此我们可以给同一个连续的存储空间取多个别名,这就诞生了C++中的引用机制
2、引用的概念
引用可以看作一个已定义变量的别名引用的语法:Type& name = var;
示例:
exp-1.cpp
#include <stdio.h> int main(int argc, char *argv[]) { int a = 4; int& b = a; b = 5; printf("a = %d\n", a); printf("b = %d\n", b); printf("&a = %p\n", &a); printf("&b = %p\n", &b); return 0; }
运行结果:
可见,a和b指向了同一内存块!
Tip:
普通引用在声明时必须用其它的变量进行初始化。
3、引用的意义
引用作为其它变量的别名而存在,因此在一些场合可以代替指针引用相对于指针来说具有更好的可读性和实用性
示例:
exp-2.cpp
#include <stdio.h> void swap_q(int& a, int& b) { int temp = a; a = b; b = temp; } void swap_p(int *pa, int *pb) { int temp = *pa; *pa = *pb; *pb = temp; } int main(int argc, char *argv[]) { int a = 4; int b = 5; swap_q(a, b); printf("a = %d\n", a); printf("b = %d\n", b); printf("******************************\n"); swap_p(&a, &b); printf("a = %d\n", a); printf("b = %d\n", b); return 0; }
Tip:
引用作为函数参数声明时可不进行初始化。
4、const引用
在C++中可以声明const引用const Type& name = var;
const引用让变量拥有只读属性
示例:
exp-3.cpp
#include <stdio.h> int main(int argc, char *argv[]) { int a = 4; const int& b = a; int* p = (int*)&b; //b = 5; //这里编译器会报错,不能修改只读变量的值 *p = 5; //这里成功修改了变量a的值 printf("a = %d\n", a); printf("b = %d\n", b); return 0; }
运行结果:
Tip:
从运行结果来看,指向变量a的指针还是能成功的改变a的值,所以,此时只是无法通过常量引用b来修改a的值!
当使用常量对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名
示例:
exp-4.cpp
#include <stdio.h> int main(int argc, char *argv[]) { const int& b = 1; //编译器会为其分配存储空间 int* p = (int*)&b; //b = 5; //这里会报错,因为常量引用为只读 *p = 5; //还是可以通过指针来更改其值 printf("b = %d\n", b); return 0; }
Tip:
使用常量对const引用初始化后将生成一个只读变量。
经典问题:
#include <stdio.h> int main() { int y = 10; const int z = y; //试图用一个变量给一个常量赋值,来定义一个常量,但是这样做,z真的是一个常量么? //验证方法: //取z的地址,让编译器为它分配空间,然后把它的地址强制去掉底层const属性,通过指针向其地址空间赋值,最后看z的输出值是否改变,如果改变说名不是常量,是只读变量! int *p = const_cast<int*>(&z); *p = 7; printf("z = %d\n", z); printf("*p = %d\n", *p); return 0; }
运行结果:
从运行结果来看,上面的z是一个只读变量,而非常量!
#include <stdio.h> int main() { char c = 'c'; char& rc = c; const int& trc = c; //用一个变量给一个常引用赋值 //已经知道,如果 const int& i = 1; 编译器会为它分配存储空间,从而生成一个只读变量 //而这里也是一样的,trc最终会是一个只读变量 rc = 'a'; printf("c = %c\n", c); printf("rc = %c\n", rc); printf("trc = %c\n", trc); return 0; }
运行结果:
5、引用的本质
思考:引用有自己的存储空间吗?
测试:
exp-5.cpp
#include <stdio.h> struct TRef { int& a; int& b; }; int main(int argc, char *argv[]) { printf("sizeof(TRef) = %ld\n", sizeof(TRef)); return 0; }
运行结果:
Tip:
从上面的结果可知,引用是有存储空间的,那么这又是为什么呢?
- 引用在C++中的内部实现是一个指针常量
- Type& name <==> Type* const name
Improtance:
C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。
从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间,这是C++为了实用性而做出的细节隐藏。
思考:
既然引用变量有自己的存储空间,如何获得它的该空间的地址?
解答:
无法通过“&”取地址符,获得“引用变量”自身的存储空间的地址,通过“&”取地址符得到的是它的目标变量的地址,引用机制在一开始设计时,就没打算让我们操作它本身,对它的所有操作都反应在目标变量上;而且,引用机制很容易被编译器优化,优化后,原来访问引用变量的代码编译成的机器代码,实际上是直接访问原变量,这样的话,编译器甚至可以不为引用变量分配空间,全部直接访问它所引用的原变量。
举例:
exp-6.cpp
#include <stdio.h> struct TRef { int& a; int& b; int& c; }; int main(int argc, char *argv[]) { int a = 1; int b = 2; int c = 3; TRef rA = {a, b, c}; printf("&a = %p\n", &a); printf("&rA.a = %p\n", &rA.a); printf("&b = %p\n", &b); printf("&rA.b = %p\n", &rA.b); printf("&c = %p\n", &c); printf("&rA.c = %p\n", &rA.c); printf("&rA = %p\n", &rA); printf("sizeof(TRef) = %ld\n", sizeof(rA)); return 0; }
运行结果:
Tip:
可见,对引用变量取地址,实际上得到的是其目标变量的地址!
6、函数返回引用
若返回栈变量不能成为其它引用的初始值
不能作为左值使用
若返回静态变量或全局变量
可以成为其他引用的初始值
即可作为右值使用,也可作为左值使用
示例:
exp-7.cpp
#include <stdio.h> int& f() { static int a = 0; //静态局部变量,函数退出后不释放 return a; } int& g() { int a = 0; //局部变量,函数退出后释放 return a; } int main() { int a = g(); int& b = g(); f() = 10; //C中编译不过, //C++中f函数返回的引用可作为左值使用,这一点类似于三目运算符 printf("a = %d\n", a); printf("b = %d\n", b); printf("f() = %d\n", f()); return 0; }
运行结果:
Tip:
首先,g函数执行后,将局部变量的引用返回并赋值给了变量a,所以a的值为0;然后,g函数执行后,将局部变量赋值给一个引用变量b,然后由于g函数执行完毕,系统释放了调用g函数时的栈空间,导致b此时成为了一个未知内存块的引用,所以访问它得到的是随机值;最后,f函数执行后将它的静态局部变量的引用返回,然后作为左值给它赋值,所以值为10。
7、C++对三目运算符做了什么?
当三目运算符的可能返回都是变量时,返回的是变量的引用当三目运算符的可能返回中有常量时,返回的是常量值
8、小结
C++中的引用可以看作变量的别名来使用C++中的常引用可以使得一个变量拥有只读属性
C++中的常引用可以用常量初始化而得到一个只读变量
C++中引用的本质是一个指针常量
相关文章推荐
- 《C++核心思想》学习笔记(2)
- c++中的左值与右值
- string 和 vector 初探
- 《C++核心思想》学习笔记(1)
- C++ 运算符重载(1)
- C++14系列(2):C/C++的时间函数
- c++线程的创建
- C/C++中函数参数传递详解(一)
- 判断IP地址,MAC地址合法性-C语言
- C++指针详解(二)
- C++ CTime COleTime的一些操作技巧
- C++,你是真实的世界
- c++里面创建对象,什么时候应该用new什么时候应该直接创建?
- C++/STL中 vector中对 “=”赋值运算符的支持
- C++Primer 【笔记】文本查询程序 TextQuery
- C++ 手动替换malloc函数,并且手动获取调用者地址
- 如何利用《C++ Primer》学习C++?
- C++/STL_中Vector的基本操作与示例
- C++多态的实现原理
- 黑马程序员———C语言———【枚举、typedef、宏、static和extern、文件操作】