一、区别与联系
char a;
char *p = a;
char &ra = a;
sizeof(a); //1
sizeof(p); //4
sizeof(ra); //1
int a;
int *p = nullptr; //right
int &ra; //error,必须初始化
int &rb = a; //right
int a;
const int *p = &a; //底层const,无法修改值
int * const p = &a; //顶层const,无法修改指向
const int &ra = a; //不能修改值
int & const ra = a; //没有意义,引用本身就不能更改指向
int a = 2;
int& ra = a;
int* p = &a;
cout << ++ra << endl; //3,等同于对a直接进行++。
cout << ++(*p) << endl; //4,先解引用后偏移,相当于对a进行++。
cout << *(++p) << endl; //乱码,偏移后p指向的是a之后的地址,是未知的。
对象通过指针获取申请的堆上内存,指针是指向动态内存区域的唯一方式,而引用实质是对象的一个别名,对象被析构之后,引用将会失效,所以可能会使得堆上的内存空间没有及时释放,造成内存泄露。
二、注意事项
1.当引用作为函数的参数时,效果和用指针作为函数参数的效果相当。当调用函数时,函数中的形参就会被当成实参变量或对象的一个别名来使用,也就是说此时函数中对形参的各种操作实际上是对实参本身进行操作,而非简单的将实参变量或对象的值拷贝给形参。
void swap1(int a, int b)
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
void swap2(int &a, int &b)
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
int main()
{
int a = 3, b = 5;
swap1(a, b); //3 5
swap2(a, b); //5 3
return 0;
}
2.通常函数调用时,系统采用值传递的方式将实参变量的值传递给函数的形参变量。此时,系统会在内存中开辟空间用来存储形参变量,并将实参变的值拷贝给形参变量,也就是说形参变量只是实参变量的副本而已;如果函数传递的参数是类的对象,系统还会调类中的拷贝构造函数来构造形参对象。而使用引用作为函数的形参时,此时传递给函数的是实参的别名而非副本,故系统不会耗费时间来在内存中开辟空间来存储形参。因此如果参数传递的数据较大时,建议使用引用作为函数的形参,这样会提函数的时间效率,并节省内存空间。
3.使用指针作为函数的形参虽然达到的效果和使用引用一样,但当调用函数时仍需要为形参指针变量在内存中分配空间,而引用则不需要这样,故在C++中推荐使用引用而非指针作为函数的参数。
4.如果在编程过程中既希望通过让引用作为函数的参数来提高函数的编程效率,又希望保护传递的参数使其在函数中不被改变,则此时应当使用常引用作为函数的参数。
三、总结
1.在引用的使用中,单纯给某个变量取别名是无意义的,引用的目的主要用于在函数参数的传递中,解决大块数据或对象的传递效率和空间不如意的问题。
2.引用传递函数的参数,能保证参数在传递的过程中不产生副本,从而提高传递效率,同时通过const的使用,还可以保证参数在传递过程中的安全性。
3.引用本身是目标变量或对象的别名,对引用的操作本质上就是对目标变量或对象的操作。因此能使用引用时尽量使用引用而非指针。
4.指针可以装地址,可以偏移,可以间接引用。引用只能实现指针间接引用的功能,如果是new的空间必须要用指针来接。