概念
- 引用不是新定义一个变量,而是给一个已经存在的变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
用法
- 引用变量
1 | int data = 0; |
常引用变量
1
2int data = 0;
int const& ref = data;//只读常引用常量
1
2const int data = 0;
const int& ref = data;指针引用
1
2
3
4
5
6
7
8int data = 0;
int *p = &data;
int* &ref = p;
cout << data << endl; //0
cout << *p << endl; //0
cout << *ref << endl; //0
cout << p << endl;//0x61fe14
cout << ref << endl;//0x61fe14引用变量的类型必须与它的实体类型一致(因为取别名要符合引用实体的身份,如果类型不一致则会报错)
引用变量使用必须要进行初始化(不然没有实体都不知道给谁取别名)
一个变量可以有多个引用(就相当于一个变量有好几个别名,这是可以的)
以pass-by-reference-to-const替换pass-by-value
- 通常情况下c++使用值传递,传递对象都是由拷贝构造函数产生,这样做会产生非常昂贵的成本。每对一个对象进行值传递,就会进行一次拷贝构造函数和析构函数。
- 考虑上述情况,可以使用引用传递来避免不必要的运行和内存开销。同时,如果希望在函数中不对对象进行任何改变,可以使用const关键字避免。
- 在c++底层,引用是通过指针实现的,因此对于内置类型如int,double,float等使用值传递会有更高的效率。
必须返回对象时,不要使用引用
考虑如下的例子,考虑在函数内生成一个对象
1
2
3
4
5
6
7
8
9
10
11
12
13class A{
int val;
public:
inline A(){};
inline A(int x):val(x){};
inline int getVal()const{
return val;
}
};
const A& operator*(const A& lhs, const A& rhs){
A* res = new A(lhs.getVal() * rhs.getVal());
return *res;
}operator*函数内生成了一个对象,并在heap上分配了内存,那么这块内存永远不会再外部释放,因为离开函数作用域这个指针将自动销毁。
那么使用下面这个函数生成临时对象也一样不可取,因为离开函数作用域,这个对象将被销毁。1
2
3
4const A& operator*(const A& lhs, const A& rhs){
A res(lhs.getVal() * rhs.getVal());
return res;
}能不能像下面这样声明一个静态对象来保存结果呢?
1
2
3
4
5const A& operator*(const A& lhs, const A& rhs){
static A res;
res = A(lhs.getVal() * rhs.getVal());
return res;
}表面上看没有太大问题,但是在使用中我们常常会像一下这样来使用:
1
2
3
4
5bool operator==(const A& lhs, const A& rhs);
A r, s, t, x;
if((r * s) == (t * x)){
//do something
}如果保存的是静态对象,那么这个表达式永远为真。表达式展开的结果是
1
if(operator==(operator*(r, s), operator*(t, x))){}
在比较时永远比较的是静态对象值,不管执行顺序是什么,对象自身永远相等。
正确的做法
- 让函数返回一个新对象
1
2
3
4inline const A operator*(const A& lhs, const A& rhs){
A res(lhs.getVal() * rhs.getVal());
return res;
}