C++剖析之引用
概念篇
引用就是给已存在的变量取一个别名,引用不开辟新空间,它和它引用的变量共用一块内存空间。
形式是在变量类型后加上引用符号,例如:
int a=9;
int& b=a; //这里b为a的引用
注意:
- 一个变量可以有多个引用;
- 引用必须在定义时初始化,引用不能为空;
- 引用一旦引用一个实体,就再不能引用其他实体。
常引用
若变量是const修饰的常变量,引用前也必须加上const
const int a = 10; //const修饰的a只可以读,不可以修改
const int& ra = a;
//若引用前没加const,引用就是错误的,属于权限放大
//(因为a本来是不可以修改的,引用后的ra却可以进行修改)
若变量是没有const修饰的变量,引用前也可以加const
int b = 10;
const int rb = b; //这里加上const相当于权限缩小,引用后的rb只可以读,不可以修改
下面介绍一个涉及类型转换的常引用
int c = 10;
double d = 10.1;
d = c; //这种隐式类型的转换的实际是:c先给一个double类型的临时变量,
//然后再把临时变量赋值给d
const double& rc = c;//切记前面一定不能丢掉const
//这里的rc是临时变量的别名,不是直接把c给rc,而是一个double类型的临时变量
//临时变量具有常性,前面就必须加上const,否则属于权限放大
注意:
- 只要有类型偏差的地方(无论是强制类型转换还是隐式转换),一般就会出现临时变量,在一引用时一定就要注意临时变量具有常性。
引用使用的场景
1.做参数
void swap(int& left,int& right)
{
int temp = left;
left = right;
right = temp;
}
int main()
{
int a = 1,b = 2;
swap(a,b);
return 0;
}
2.做返回值(特殊情况下)
int& add(int a,int b)
{
int c= a + b;
return c;
}
int main()
{
int ret = add(1,5);
return 0;
}
//这里返回的是c的别名
//但因为函数调用结束后,栈帧就会被销毁,是无法读取到c的内存的,
//所以这时读取到的应该是一个随机值
注:
- 当有一个函数的调用,就会在空间上向下建立一个栈帧;当函数调用结束时,栈帧就会被销毁。
- 其实我们会发现,这时候的引用做返回值是有问题的,因为它返回的是一个已经被销毁的空间上的别名;
- 若要用引用做返回值,可以在定义函数返回对象前加static,这样相当于把变量存储在了静态区,当函数调用结束栈帧销毁时就不会影响它。
- 综上所述,引用的使用一般体现在:参数和返回值是比较大的变量,这样可以提高效率。
指针和引用的区别
- 引用是给变量定义了一个别名,没用空间的开辟;而指针需要开辟一个地址来存储变量。
- 引用在定义时必须初始化;指针不做要求。
- 引用一旦引用一个实体,就再不能引用其他实体;而指针可以在任何时候指向任何一个同类型实体。
- 不存在NULL引用;但有NULL指针。
- 在sizeof中含义不同:引用的结果为引用类型的大小;但指针始终是地址空间所占字节数(32位平台下占4字节,64平台下占8字节)。
- 引用自加指引用的实体增加1;指针自加即指针向后偏移一个类型的大小。
- 有多级指针,但没有多级引用。
- 引用相较于指针用起来更加安全一些。