首页 > 代码库 > C++ 引用
C++ 引用
C++11 之后,C++的引用扩充为 非常量左值引用,常量左值引用,非常量右值引用,常量右值引用。
什么是左值?什么是右值?左值、右值都是针对表达式而言的,左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时就不存在的临时对象。一个区分左值、右值的便捷方法是:看能不能对表达式取地址,如果能,则为左值,否则为右值。
什么是引用?引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字。定义了一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的。
引用又分为常量引用、非常量引用,它们的区别是:通过非常量引用可以对与之绑定的对象做所有操作,而对通过常量引用,则不可以对与之绑定的对象做任何修改操作。
引用在定义时需要同时初始化,即完成与一个对象的绑定。以上四类引用对可以与之绑定的对象是有要求的。
非常量左值引用(T & = x) : x只能是一个T类型的非常量左值。
常量左值引用 (const T & = x) : x可以是左值,也可以是右值。x的类型可以是T类型以及所有可以隐式转换为T类型的其它类型。x既可以是常量也可是非常量。
非常量右值引用(T && = x) : 如果X是T类型的,则必须是非常量右值。如果X是可以隐式转换为T类型的其它类型,则左值、右值都可以。
常量右值引用(const T && = x) :可以指向T类型的常量、非常量右值。如果X是可以隐式转换为T类型的其它类型,则左值、右值都可以。
当引用绑定的对象被销毁后,这个引用就失效了,使用失效的引用是未定义的行为,一般就是程序崩溃。所以引用绑定的对象啥时候被销毁是需要关注的。引用绑定左值没啥问题,但指向右值,不是说右值在表达式结束后就销毁了吗?那定义的这个引用还有啥用?引发未定义的行为?引用定义有两个场景:函数参数传递、其它。
先说函数参数传递。比如函数声明为 void fun(const string & param),调用时使用右值初始化param: fun( string()); 。可以看到,string()虽然是个右值,fun(string())这个表达式结束后被销毁了,但这时fun函数已经运行完了,所以在fun运行期间,引用param一直有效。所以,这个场景引用绑定右值完全没有问题。
再说其它(函数作用域、全局作用域、类作用域内定义引用),以函数作用域为例:
class test1{public: test1(int ){ cout<<"cc"<<endl; } test1(const test1 &) { cout<<"copy"<<endl; } ~test1(){cout<<"destroy"<<endl;}};void fun(){ const test1 &x = test1(2);
test1 y(x);}
以上代码中,x绑定了一个右值,而这个右值按照常规,在const test1 &x = test1(2); 这个表达式结束后就被销毁了,但实际上却没有。在这种情况下,这个右值的生命期被延长了,test1(2)等同于一个匿名的左值变量,在退出它的作用域时才被销毁。其行为等同于以下代码:
void fun(){ test1 tmp(2); const test1 &x = tmp; test1 y(x);}
以上讨论的右值不包括字面常量,如果右值是字面常量,编译器会为其定义一个临时变量来存储它的值,然后引用绑定的就是这个临时变量。当然,这个临时变量也是个右值,所以其生命期的规律就是普通右值的规律。
常量左值引用 const T & = x ,x还可以指向不是T类型的左值、右值,只要x的类型可以隐式转换为T类型。其原理是这样的:编译器将x隐式转换为T类型的一个临时变量,然后 常量左值引用绑定的就是这个临时变量。这个临时变量当然是个右值。右值的生命期的规律上面已经讲过。
class test1{public: test1(int ){ cout<<"cc"<<endl; } test1(const test1 &) { cout<<"copy"<<endl; } ~test1(){cout<<"destroy"<<endl;}};int main(){ test1 x(3); //test1 && y = x; //error,右值引用只能指向右值 int i = 3; test1 && y = i; //right,y指向的是用i生成的一个临时变量:右值 system("pause"); return 0;}
C++ 引用