c++11中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能,用的不多,每次看过了就忘了,整理下;
1、左值和右值:
左值是指表达式结束后依然存在的持久化对象;
右值是指表达式结束时就不再存在的临时对象;
比方:
int i=0;// i是左值, 0是右值
2、左值引用:
c++98中的引用很常见了,就是给变量取了个别名,在c++11中,因为增加了右值引用(rvalue reference)的概念,所以c++98中的引用都称为了左值引用(lvalue reference)。
int a = 10;
int& refA = a; // refA是a的别名, 修改refA就是修改a, a是左值,左移是左值引用
int& b = 1; //编译错误! 1是右值,不能够使用左值引用
3、右值引用,c++11中的右值引用使用的符号是&&,如:
int&& a = 1; //实质上就是将不具名(匿名)变量取了个别名
int b = 1;
int && c = b; //编译错误! 不能将一个左值复制给一个右值引用
class A {
public:
int a;
};
A getTemp()
{
return A();
}
A && a = getTemp(); //getTemp()的返回值是右值(临时变量)
总结一下,其中T是一个具体类型:
左值引用, 使用 T&, 只能绑定左值;
右值引用, 使用 T&&, 只能绑定右值;
常量左值, 使用 const T&, 既可以绑定左值又可以绑定右值;
已命名的右值引用,编译器会认为是个左值;
编译器有返回值优化,但不要过于依赖;
Q:下面涉及到一个问题:x的类型是右值引用,指向一个右值,但x本身是左值还是右值呢?C++11对此做出了区分:
Things that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue.
4、移动构造函数
在c++11 之前,类包括构造函数,析构函数,拷贝构造函数,赋值构造函数。对于存在指针变量的类来讲,其拷贝构造函数,赋值构造函数必须实现指针变量的深拷贝,这可能会涉及到比较耗时的操作(比如string 类存储了一个超长字符串,在调用其拷贝构造或赋值构造时需要超长字符串的拷贝)。移动构造函数相对拷贝构造函数和赋值构造函数而言不会进行成员变量的深拷贝而是交换其所有权,这样就避免的拷贝时带来的性能损耗。移动构造的函数声明如下:
class_name ( class_name && );
5、右值引用的意义:
直观意义:为临时变量续命,也就是为右值续命,因为右值在表达式结束后就消亡了,如果想继续使用右值,那就会动用昂贵的拷贝构造函数。(关于这部分,推荐一本书《深入理解C++11》)
右值引用是用来支持转移语义的。转移语义可以将资源 ( 堆,系统对象等 ) 从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高 C++ 应用程序的性能。临时对象的维护 ( 创建和销毁 ) 对性能有严重影响。
转移语义是和拷贝语义相对的,可以类比文件的剪切与拷贝,当我们将文件从一个目录拷贝到另一个目录时,速度比剪切慢很多。
6、std::move和std::forward的区别:
std::move执行一个无条件的转化到右值。它本身并不移动任何东西;
std::forward把其参数转换为右值,仅仅在那个参数被绑定到一个右值时;
std::move和std::forward在运行时(runtime)都不做任何事。
参考:[c++11]我理解的右值引用、移动语义和完美转发 https://www.jianshu.com/p/d19fc8447eaa
C++ 11 左值,右值,左值引用,右值引用,std::move, std::foward : https://blog.csdn.net/xiaolewennofollow/article/details/52559306 (关于std::move()和std::forward 推荐一本书:《effective modern C++》)