本帖最后由 tyyhmtyyhm 于 2018-4-28 10:02 编辑
第一,引用的意义1、引用作为其它变量的别名而存在,在一些场合可以代替指针; 2、引用相对于指针来说具有更好的可读性和实用性。(实例可以见之前的交换两个变量的值程序) 第二,引用的本质思考思考1:引用连接的两个变量地址相同吗? 之前我们理解b是a的别名,那么变量b有地址吗?还是跟a地址一样呢? 我们运行程序: #include <iostream> using namespace std;
int main() { //const int a=10;//必须初始化
int a=10; int &b=a; //b很像一个const常量
cout<<"a="<<a<<endl;//打印a的值 cout<<"b="<<b<<endl;//打印b的值
//a和b是同一块内存空间的“门牌号” cout<<"&a="<<&a<<endl;//打印a的地址 cout<<"&b="<<&b<<endl;//打印b的地址
system("pause"); return 0; } 运行结果: a=10 b=10 &a=002EF9FC &b=002EF9FC 请按任意键继续. . .
发现a和b的地址是一样的,这说明a和b是同一块内存空间的“门牌号”。
思考2:引用占不占内存空间呢? 我们知道引用依附于一个变量,那么引用定义的变量占不占内存空间呢? 我们通过结构体来研究一下这个问题: 在结构体中定义两个引用变量,看下这两个引用变量占据多大的内存,也就是这个看下这个结构体占据多大的内存。 #include <iostream> using namespace std; struct Teacher { char name[64];//64字节 int age; //4个字节 int &a; //多少个字节? int &b; }; int main() { cout<<"sizeof(Teacher)="<<sizeof(Teacher)<<endl;
system("pause"); return 0; } 运行结果: sizeof(Teacher)=76 请按任意键继续. . .
通过运行我们发现结构体占了76个字节,也就是说每个引用占了4个字节内存,这个和指针是一样的!指针也是占4个字节内存的! 另外,我们知道单独定义引用时,必须初始化:说明引用很像一个const常量。因为定义常量时 const int a = 10;必须初始化,进行赋值,如果不初始化就会报错。 再加上这部分我们探讨发现引用很像一个指针,占据4个字节的内存。 先对上面两部分做个小结: 我们看到引用变量b作为a的别名,二者内存地址相同,但是为什么又分别占据内存呢?援引网上的一句话:由于引用本身就是目标的一个别名,引用本身的地址是一个没有意义的值,所以在c++中是无法取得引用的内存地址的。取引用的地址就是取目标的地址,c++本身就根本不提供获取引用内存地址的方法。 第三,引用的本质1、引用在C++中的内部实现是一个常指针; Type &name <-----> Type *const name 2、C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同; 3、从使用的角度,引用会让人误认为其只是一个别名,没有自己的内存空间(跟目标公用一个内存空间),这是C++为了实用性而做出的细节隐藏,就是说C++不想让人们找到引用自己真实的地址,即使你去取引用变量b的地址,也只是目标变量a的地址。 想下指针变量具有一个地址,指针指向的空间也有一个地址,没有隐藏,很多指针指向同一个对象时,如果一个指针对对象进行了析构,就会使其他指针变成“野指针”,这就很危险!(该点根据自己理解,不当之处请指正!)
看这两个函数: void func(int &a) //函数一 { a=5; } void func(int *const a) //函数二 { *a=5 } 当执行函数一的时候,是C++编译器在背后在背后通过函数二的方式进行了编译。在编译时C++看到a是一个引用,就会翻译成函数二的形式。再看下面的程序: #include <iostream> using namespace std;
int modifyA(int &a1) { a1=100; } int modifyA2(int *a1) { *a1=200;//【*实参的地址】间接修改实参的值 } int main() { int a=10;
//指向这个函数调用的时候,我们程序员不需要取a得地址 modifyA(a); cout<<"a="<<a<<endl;
//如果是指针,需要我们程序员手工的取实参的地址 modifyA2(&a); cout<<"a="<<a<<endl;
system("pause"); return 0; }
当我们执行函数modifyA的时候,是C++编译器自动的取了a的地址传递给了a1。执行modifyA函数调用的时候,我们就不需要再取a的地址了,C++编译器帮我们取了。如果是执行modifyA2函数,使用指针,就需要传递a的地址。 第四,引用小结先来看间接赋值成立的三个条件: 1、定义两个变量(一个实参一个形参) 2、建立关联,实参取地址传给形参 3、*p形参去间接修改实参的值 小结: 1、引用在实现上,只不过是把间接赋值成立的三个条件的后两步合二为一。当实参传给形参引用的时候,只不过是C++编译器帮我们程序员自动取了一个实参地址,传给了形参引用(常量指针)。 2、当我们使用引用语法时,我们不去关心编译器引用是怎么做的。当我们分析奇怪的语法现象时,我们才去考虑C++编译器是怎么做的。
【C++】笔记系列均为原创,转载请注明转自微号:依法编程 更多精彩资料,请关注!
|