c++(6)----移动语义
为什么需要移动语义
一些场景
#include <iostream> #include <cstring> using namespace std; class A{ public: A():i(new int[20]){ cout<<"class A construct!"<<endl; } A(const A &a):i(new int[20]){ memcpy(a.i,i,20*sizeof(int)); cout<<"class A copy!"<<endl; } ~A(){ delete []i; cout<<"class A destruct!"<<endl; } private: int *i; }; A get_A_value(){ return A(); } void pass_A_by_value(A a){ } int main(){ A a = get_A_value(); return 0; }
g++ test01.cpp -o main -fno-elide-constructors -std=c++11 运行结果如下: class A construct! class A copy! class A destruct! class A copy! class A destruct! class A destruct!

左值:有标识符、可以取地址的表达式
如:变量、函数或数据成员的名字
返回左值引用的表达式,如 ++x、x = 1、cout << ' ' ,字符串字面量如"hello world"
左值可以绑定到左值引用的参数,如 T&。一个常量只能绑定到常左值引用,如 const T&。
纯右值 : prvalue 是没有标识符、不可以取地址的表达式,一般为“临时对象”。
返回非引用类型的表达式,如 x++、x + 1、make_shared<int>(42)
除字符串字面量之外的字面量,如 42、true
右值引用:
T&&
c++11 之前右值可以绑定到常左值引用(const T&)中,单不能绑定到非常左值引用(T &)中。
C++11引入右值引用 T&&,可以将右值绑定到又值引用类型中。
右值引用类型是左值,类型是右值引用的变量是一个左值!
#include <iostream> #include <cstring> using namespace std; class A{ public: A():i(new int[20]){ cout<<"class A construct!"<<endl; } A(const A &a):i(new int[20]){ memcpy(a.i,i,20*sizeof(int)); cout<<"class A copy!"<<endl; } // 添加一个右值引用类型参数的构造函数 A(A &&a) noexcept{ i=a.i; if(i){ a.i=nullptr; } cout<<"class A(A &&) copy!"<<endl; } ~A(){ if(i) delete []i; cout<<"class A destruct!"<<endl; } private: int *i; }; A get_A_value(){ return A(); } void pass_A_by_value(A a){ }
g++ test01.cpp -o main -fno-elide-constructors -std=c++11
int main(){ A a = get_A_value(); return 0; } // 输出 class A construct! class A(A &&) copy! class A destruct! class A(A &&) copy! class A destruct! class A destruct! /** A() 得到一个右值,匹配到右值类型的参数 **/
int main(){ A a1; A a2 = a1; return 0; } // 输出 class A construct! class A copy! class A destruct! class A destruct! /** a1 是一个左值 **/
int main(){ A a1; A a2 = std::move(a1); return 0; } //输出 class A construct! class A(A &&) copy! class A destruct! class A destruct! /** std::move(a1) ; 将一个左值引用强制转换乘一个右值引用 **/
移动语义的意义
使得在 C++ 里返回大对象(如容器)的函数和运算符成为现实,因而可以提高代码的简洁性和可读性
实现步骤:
1、分开拷贝构造 和 移动构造
2、右swap成员函数,支持和另外一个对象快速交换
3、在你的对象的名空间下,应当有一个全局的 swap 函数,调用成员函数 swap 来实现交换。支持这种用法会方便别人(包括你自己在将来)在其他对象里包含你的对象,并快速实现它们的 swap 函数。
4、实现通用的operator=
5、应当标为 noexcept。这对移动构造函数尤为重要
6、移动构造函数应当从另一个对象获取资源,清空其资源,并将其置为一个可析构的状态。
浙公网安备 33010602011771号