引用分为左值引用和右值引用(什么是左值和右值?参考文章:【译】理解C和C++中的左值和右值)。引用是为一个已存在的对象或函数取的别名。如:
- int func()
- {
- return 4;
- }
-
- int main(int argc, char* argv[])
- {
-
- int a = 10;
-
- // lvalue reference initialize
- int& lr_a = a;
- // rvalue reference initialize
- int&& rr_a = 1 + 2;
- int&& rr_f = func();
-
- return 0;
- }
注意:
- int main(int argc, char* argv[])
- {
-
- int a = 10;
- int& arr[3] = {a, a, a};
- int& *p = &lr_a;
-
-
- return 0;
- }
-
-
- PS F:\Jungle\1.Program\4.C++\5.C++11\02.reference> g++ -o app .\main.cpp
- .\main.cpp: In function 'int main(int, char**)':
- .\main.cpp:30:15: error: declaration of 'arr' as array of references
- int& arr[3] = {a, a, a};
- ^
- .\main.cpp:31:11: error: cannot declare pointer to 'int&'
- int& *p = &lr_a;
引用折叠(或者引用坍塌), 是指当引用指向引用的时候,此处的引用可以是左值也可以是右值引用,最终的类型会有部分被折叠到一起。
- typedef int& lref;
- typedef int &&rref;
- int n;
-
- lref &r1 = n; // type of r1 is int&
- lref &&r2 = n; // type of r2 is int&
- rref &r3 = n; // type of r3 is int&
- rref &&r4 = 1; // type of r4 is int&&
一个左值,代表一个在内存中占有确定位置的对象,简言之,左值在内存中有地址。左值引用可以作为一个已存在的对象的别名来使用。
- int main(int argc, char* argv[])
- {
- int a = 10;
- int& b = a;
- std::cout << " a address : " << &a << std::endl;
- std::cout << " b address : " << &b << std::endl;
-
- std::cout << " a = " << a << "\t b = " << b << std::endl;
-
- std::cout << "change a to 11: ";
- a = 11;
- std::cout << " a = " << a << "\t b = " << b << std::endl;
-
- b = 12;
- std::cout << "change b to 12: ";
- std::cout << " a = " << a << "\t b = " << b << std::endl;
-
- const int& c = a;
- // c = 13; error : cannot change a through reference to const
- return 0;
- }
运行上述代码:
- a address : 0x22fe44
- b address : 0x22fe44
- a = 10 b = 10
- change a to 11: a = 11 b = 11
- change b to 12: a = 12 b = 12
可以看到,左值引用和原对象a的地址是一样的。改变b实际上会改变a,反之亦然。
右值是什么呢?我们这么来定义:非左即右。一个对象不是左值就是右值,如果能够通过左值的定义判断一个对象是左值,那么它就是左值;否则就是右值。通过上述左值的定义也可以看出,右值在内存中没有确定位置的地址。
C++11推出了右值引用。右值引用可以延长一些临时对象的生命周期。右值引用的特点是:它将让其所绑定的右值重获新生,即使该右值本身是一个临时变量,但是它本身却不能绑定任何左值。
- int a = 10;
- // int &&r0 = a; error : 不能将右值引用绑定到左值
- int&& r1 = 1 + 2; // ok, 右值引用绑定到一个临时对象
右值引用常用于移动语义(移动构造、移动赋值和完美转发),以节省深拷贝引起的开销。
