--事物的难度远远低于对事物的恐惧!
在上一节中我们知道,引用是变量的别名,操作这个别名就相当于操作这个变量,而在C++中,引用更多时候可以在一些场合代替指针,并且相对指针来说,引用具有更好的可读性和实用性。
下边来看看一个老生常谈的交换函数
#include <iostream> using namespace std; //引用方式 void swap1(int& a, int& b) //注意:函数参数中的引用不需要初始化 { int tmp = a; a = b; b = tmp; } //指针方式 void swap2(int* a, int* b) { int tmp; tmp = *a; *a = *b; *b = tmp; } int main(int argc, char const *argv[]) { int a1 = 12; int b1 = 23; int a1 = =12; int b2 = 23; swap1(a1, b1); cout << "a1 = " << a1 << ", " << "b1 = " << b1 << endl; swap2(&a2, &b2); cout << "a2 = " << a2 << ", " << "b2 = " << b2 << endl; return 0; }
编译输出如下,可以看到,两个函数均正确的交换了变量值,从使用角度来说,引用更令人感到简洁愉快。
下边我们来看看特殊的引用
const引用
-C++中申明的const引用让引用这个变量拥有只读属性(注意是引用这个变量,不能通过引用这个变量修改内存空间的值,但是还是可以通过用来初始化引用的那个变量去修改)
-格式为:const Type& name = value;
int a = 12; const int& b = a; int* p = (int*)&b; // b = 0; //error, 引用b为只读变量 a = 23; cout << a << endl; *p = 34; cout << a << endl;编译输出如下:
-当用常量对const引用进行初始化时,C++编译器会为常量分配空间,并把引用名作为这段空间的别名
#include <iostream> using namespace std; int main(int argc, char const *argv[]) { const int& value = 1; int *p = (int *)&value; // value = 12; //error,value为只读变量 *p = 34; cout << "&value = " << &value << endl; cout << "value = " << value << endl; return 0; }
编译输出如下,可以看到,C++编译器已经为value分配了内存空间,同时可以通过指针修改这段内存空间的内容:
结论:使用常量初始化const引用,生成的是一个只读变量。
一个问题:引用有自己的存储空间吗?
#include <iostream> using namespace std; struct TRef { char& r; }; int main(int argc, char const *argv[]) { cout << "sizeof(TRef) = " << sizeof(TRef) << endl; cout << "sizeof(char*) = " << sizeof(char*) << endl; return 0; }编译执行下上边的代码(我的g++为64为编译器)
从输出结果来看,引用的大小跟指针的大小一样。
本质就是:引用在C++中的内部实现是一个指针常量
注意:
-C++编译器在编译过程中用指针常量作为引用的内部实现,因此引用所占用的内存空间大小与指针相同。
-从使用角度,引用只是一个别名,C++为了实用性而隐藏了引用的存储空间这一细节。
-函数不能返回局部变量的引用(类似于返回局部变量的指针)
引用的意义:可在大多数情况下代替指针,又能避免由于指针操作不当带来的内存错误。
总结:
-引用作为变量别名而存在大多数情况是为了代替指针
-const引用可以使得变量具有只读属性
-引用在编译器内部使用指针常量实现
-引用的最终本质为指针,占用的内存空间与指针大小相同
-引用可以尽可能的避开内存错误