第一次看到C++中的const引用传递有点困惑。在我的理解中,之所以用引用传递,是因为函数可以修改传递的参数。而不是像值传递那样,只创建参数的副本,无法修改参数。但是加上const,又表示不能修改该参数。那这const引用传递有啥用呢?
先不讨论引用传递和值传递。直接默认使用引用传递,看非const引用传递和const引用传递的区别。
实际例子
考虑如下代码
#include<iostream>
using namespace std;
int fun1(int& pi)
{
cout<<"pi:"<<pi<<endl;
cout<<"pi地址:"<<&pi<<endl;
cout<<endl;
return pi;
}
int fun2(const int& pi)
{
cout<<"pi:"<<pi<<endl;
cout<<"pi地址:"<<&pi<<endl;
cout<<endl;
return pi;
}
int main()
{
int val_i = 3;
double val_d = 3.14;
cout<<"val_i地址:"<<&val_i<<endl;
fun1(val_i);
cout<<"val_d地址:"<<&val_d<<endl;
fun1(val_d);
fun1(3);
fun1(3.14);
// cout<<"val_i地址:"<<&val_i<<endl;
// fun2(val_i);
// cout<<"val_d地址:"<<&val_d<<endl;
// fun2(val_d);
// fun2(3);
// fun2(3.14);
return 0;
}
这里定义了两个函数fun1和fun2,分别是非const值传递和const值传递。
先测试fun1,编译
g++ -o main main.cc -std=c++11
编译将报错:
main.cc: In function ‘int main()’:
main.cc:31:15: error: invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’
fun1(val_d);
^
main.cc:8:5: note: initializing argument 1 of ‘int fun1(int&)’
int fun1(int& pi)
^
main.cc:32:11: error: invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’
fun1(3);
^
main.cc:8:5: note: initializing argument 1 of ‘int fun1(int&)’
int fun1(int& pi)
^
main.cc:33:14: error: invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’
fun1(3.14);
^
main.cc:8:5: note: initializing argument 1 of ‘int fun1(int&)’
int fun1(int& pi)
意思是用int型右值初始化非const引用是无效的。这是因为,C++中非const的引用参数必须用与其类型一致的对象/量来初始化,所以传递double变量是错误的。数字3是一个字面值,也就是个右值,而函数要求的是一个具体的int类型的对象/变量,需要左值,所以传递3也是错误的。只有第1个调用正确。
const引用传递可以完美解决上面问题。它可以在某些情况下生成临时变量,比如下面两种情况:
1、实参类型不正确,但可以转换为正确类型;
2、实参类型正确,但不是左值。
把上面代码改为用fun2,则编译通过,执行结果如下:
val_i地址:0x7ffff930ca68
pi:3
pi地址:0x7ffff930ca68
val_d地址:0x7ffff930ca70
pi:3
pi地址:0x7ffff930ca6c
pi:3
pi地址:0x7ffff930ca6c
pi:3
pi地址:0x7ffff930ca6c
可以看到,在传递val_d前后数据地址不同。这正是由于数据类型不匹配,创建了临时变量。
const引用传递的作用
在c++中当函数参数为引用时,如果传递的实参与函数参数类型不匹配,那么就要将参数类型定义为const,此时函数将会产生一个临时变量,临时变量自动转化为函数参数类型。否者将报错。
如果传递的实参是一个临时变量,那么就要将函数参数定义为const类型。否则也会报错。
即使为了代码易读,可以保证绝对不会出现字面值,有时也不得不用const引用传递。如果你写了个函数为了保护参数加了const,但函数里面调用了另一个参数没const的函数,那么这里估计就要出错,因为const实参不能传递给非const形参。虽然你能保证自己的代码不冲突,但不能保证别人的代码,尤其是合作的时候每人写一个部分。
非要用引用传递吗?
引用传递一般用于修改传递的实参,那如果不需要修改时,一律用值传递是否可以?答案是有时还真不行。原因如下:
- 引用传递可减少类的构造和析构次数,减少程序开销。
- 引用传递可以避免类继承时的截断问题,确保多态性。
详见下一篇文章。
小结
不需要改动的参数,如果要通过引用传递,全都加上const最安全。