C++11 右值引用&&、移动语义std::move、完美转发std::forward
参考:https://blog.csdn.net/HR_Reborn/article/details/130363997
#pragma once class Array { public: Array() : size_(0), data_(nullptr){ } Array(int size) : size_(size) { data_ = new int[size_]; } // 复制构造函数 (深拷贝构造) Array(const Array& temp_array) { size_ = temp_array.size_; data_ = new int[size_]; for (int i = 0; i < size_; i++) { data_[i] = temp_array.data_[i]; } } // 赋值构造函数 (深拷贝赋值) Array& operator=(const Array& temp_array) { delete[] data_; size_ = temp_array.size_; data_ = new int[size_]; for (int i = 0; i < size_; i++) { data_[i] = temp_array.data_[i]; } //返回对象的引用,是为了做链式处理: f1 = f2 = f3; return *this; } // 移动构造函数 Array(Array&& temp_array) { cout << "移动构造函数\n"; data_ = temp_array.data_; temp_array.data_ = nullptr;// 为防止temp_array析构时delete data,提前置空其data_ } // 移动赋值构造函数 Array& operator=(Array&& temp_array) { cout << "移动赋值构造函数\n"; data_ = temp_array.data_; temp_array.data_ = nullptr;// 为防止temp_array析构时delete data,提前置空其data_ return *this; } ~Array() { delete[] data_; } public: int *data_; int size_; }; // --> 测试 完美转发 void B(int&& ref_r) { ref_r = 1; } // A、B的入参是右值引用 // 有名字的右值引用是左值,因此ref_r是左值 void A(int&& ref_r) { //B(ref_r); // 错误,B的入参是右值引用,需要接右值,ref_r是左值,编译失败 B(std::move(ref_r)); // ok,std::move把左值转为右值,编译通过 B(std::forward<int>(ref_r)); // ok,std::forward的T是int类型,属于条件b,因此会把ref_r转为右值 } void func1(int& i){ cout << "参数是左值," << i << endl; } void func1(int&& i){ cout << "参数是右值," << i << endl; } void func(int&& i){ //注意:形参 i是左值 func1(i);//这样怎么都是调用的 左值引用的 函数。为解决,用完美转发std::forward } // 1.如果模板中(包括类模板、函数模板)的参数为 T&& 参数名,那么函数既可以接受左值引用,也可以接受右值引用。 //2. std::forward<T>(参数), 用于转发参数。若参数是左值,转发后仍然是左值,若参数是右值,转发后仍然是右值。 template<class T> void func(T&& t){ func1(std::forward<T>(t)); } // <-- 测试 完美转发 class TestRightValueRef { public: TestRightValueRef() = delete; ~TestRightValueRef() = delete; static void testAll(){ //test02(); test03(); } static int foo(){ return 0; } static void test01(){ /* 左值,lvalue,就是赋值符号左边的值,如a=5,a就是左值,但是准确的来说,左值是表达式(不一定是赋值表达式)后仍然存在的持久对象。 右值,rvalue,右边的值,是指表达式结束就不存在的临时对象。 纯右值,prvalue,用于计算的或者用于初始化对象的的右值。例如 int a = 5; // 5是右值 注意 字符串 "abc" 这个不是右值,是指针 ( 在介绍将亡值之前对左右和右值做个总结,有地址的变量就是左值,没有地址的字面值、临时值就是右值。) 将亡值,xvalue, 是C++11为了引入右值引用而提出的概念,与纯右值的不同点在于,将亡值是即将被销毁、却能够被移动的值。例如,函数中返回的对象。 有地址的变量就是左值,没有地址的字面值或临时值就是右值。 */ /* 左值引用:只能指向左值。不能指向右值的就是左值引用 右值引用:只能指向右值,不能指向左值,符号是&& */ int a = 5;// a 是左值, 5是右值 int b = foo();// b 是左值, foo()是右值 int& ra = a; //左值引用 int& rb = b; //左值引用 int&& rc = 5; //右值引用 int&& rd = foo(); //右值引用 //int& r = 5; //不能指向右值,编译报错 //int&& r = a; //不能指向左值,编译报错 //但const 左值引用 都可以 const int& rf1 = a; const int& rf2 = 3; const int& rf3 = foo(); //为什么?因为const 左值引用不会修改指向值,因此可以指向右值,这也是为什么要使用const &作为函数参数的原因之一,如std::vector的push_back: //void push_back(const value_type& val); //如果没有const,vec.push_back(5)这样的代码就无法编译通过了。 //左值引用默认只能指向左值,但是加了const的情况下可以指向右值,那么右值引用有没有类似的机制来指向左值呢?有的,就是std::move。 //std::move 唯一的功能是把左值强制转换为右值,可以让右值引用指向左值,等于一个强制类型转换。 int&& rg = std::move(a); //注意,被声明出来的左、右值引用都是左值,因为被声明出的左右值引用是有地址的,也位于等号左边。 // 所以,ra,rb,rc,rd 都是左值 /* 总结: 从性能上讲,左右值引用没有区别,传参使用左右值引用都可以避免拷贝。 右值引用可以直接指向右值,也可以通过std::move指向左值;而左值引用只能指向左值(const左值引用也能指向右值)。 作为函数形参时,右值引用更灵活。虽然const左值引用也可以做到左右值都接受,但它无法修改,有一定局限性 void f(const int& n) { n += 1; // 编译失败,const左值引用不能修改指向变量 } void f2(int && n) { n += 1; // ok } int main() { f(5); f2(5); } ———————————————— 版权声明:本文为CSDN博主「HR_Reborn」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/HR_Reborn/article/details/130363997 */ } //在实际场景中,右值引用和std::move被广泛应用于在STL和自定义类中实现移动语义,避免拷贝,从而提升程序性能。 static void test02(){ // 例1:Array用法 Array a(2); // 做一些操作 //..... // 左值a,用std::move转化为右值 Array b(std::move(a));//会调用移动构造函数 //std::move(a) 后 a 不应再被使用,会报错。因为nullptr //b = a; //error Array c; Array d; c = d;//调用赋值构造函数 c = std::move(d);//调用移动赋值构造函数 } static void test03(){ //完美转发 /* 完美转发std::forward 和std::move一样,std::forward本质上是进行类型转换,即左值右值之间的转换,与move相比,forward更强大,move只能转出来右值,forward都可以。 std::forward<T>(u)有两个参数:T与 u。 a. 当T为左值引用类型时,u将被转换为T类型的左值; b. 否则u将被转换为T类型右值。 举个例子,有main,A,B三个函数,调用关系为:main->A->B。 */ int a = 5; A(std::move(a)); cout << "-------------\n"; func(a); func(5); } };
常记溪亭日暮,沉醉不知归路。兴尽晚回舟,误入藕花深处。争渡,争渡,惊起一滩鸥鹭。
昨夜雨疏风骤,浓睡不消残酒。试问卷帘人,却道海棠依旧。知否?知否?应是绿肥红瘦。