ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward)

2022-01-09 20:31:24  阅读:204  来源: 互联网

标签:arr cout 右值 int lvalue move 左值


c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward)

c++的左值,右值 精辟总结

当一个对象被用作右值的时候,使用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)左值右值,完美转发参考文档

左值持久,右值短暂;move:显示地将一个左值转换为对应右值的引用类型,还可以获取绑定到左值上的右值引用,int&& rr3 = std::move(rrl); 使用move就意味着除了对rrl赋值或销毁它外,我们不再使用它。

std::forward<T>()与std::move()相区别的是,move()会无条件的将一个参数转换成右值,而forward()则会保留参数的左右值类型,可以使用std::forward实现完美转发

移动语义解决了无用拷贝的问题:移动构造函数

右值引用:函数的返回值

int& 左值引用

int&& 右值引用

c++中无用拷贝的情况

 /*类里面 没有移动构造函数
 这样就会使用 copy construct function,会导致大量无用的 memory copy。
 */
 class Test {  
 public:
     string desc; 
     int * arr{nullptr};
     Test():arr(new int[5000]{1,2,3,4}) { 
         cout << "default constructor" << endl;
     }
     Test(const Test & t) {
         cout << "copy constructor" << endl;
         if (arr == nullptr) arr = new int[5000];
         copy(t.arr,t.arr+5000, arr);
     }
     ~Test(){
         cout << "destructor " << desc << endl;
         delete [] arr;
     }
 };
 ​
 Test createTest() {
     return Test();
 }
 ​
 int main(){
 ​
     Test reusable;
     reusable.desc = "reusable";
     Test duplicated(reusable);
     duplicated.desc = "duplicated";
 ​
     Test t(createTest());
     t.desc = "t";
 ​
     cout<<"end"<<endl;
 }
 ​

运行结果

 default constructor
 copy constructor
 default constructor
 end
 destructor t
 destructor duplicated
 destructor reusable

使用移动语义避免无用的拷贝

 /*使用移动 construct function,避免无用的memory copy。
 */
 ​
 class Test {   
     public:
     string desc;
     int * arr{nullptr};
     Test():arr(new int[5000]{1,2,3,4}) { 
         cout << "__default constructor" << endl;
     }
     Test(const Test & t) {
         cout << "__copy constructor" << endl;
         if (arr == nullptr) arr = new int[5000]; //在这里要将 t.arr 置为空,因为经过move之后,我们认为不在使用这个值了,避免在新的对象中把指针释放后,原来的对象中存在野指针的现象
         copy(t.arr,t.arr+5000, arr);
     }
     Test(Test && t): arr(t.arr) {
         cout << "__move constructor" << endl;
         t.arr = nullptr;
     }
     ~Test(){
         cout << "..destructor " << desc << endl;
         delete [] arr;
     }
 };
 ​
 Test createTest(string str) {
     Test rt;
     rt.desc = str;
     cout<<"createTest:"<<&rt<<endl;
     return rt;
 }
 ​
 void main(){
     Test reusable;
     reusable.desc = "reusable";
     cout<<"reusable.arr "<<reusable.arr<<endl;
     
     Test duplicated(std::move(reusable));
     duplicated.desc = "duplicated";
     cout<<"reusable.arr "<<reusable.arr<<endl;
     cout<<"duplicated.arr "<<duplicated.arr<<endl;
 ​
     cout<<"rvalue--"<<endl;
     Test&& rt1 = createTest("rval");      //使用右值引用接收
     cout<<"rt1.arr "<<rt1.arr<<endl;
     
     cout<<"no rvalue--"<<endl;
     Test rt2 = createTest("normalVal");      //不使用右值引用接收,可以看到这里比使用右值引用接收 多了一次构造和析构(createTest中的临时对象)
     cout<<"createTest:"<<&rt2<<endl;        //尴尬,其实这里编译器已经做了优化了,可以看到第地址一样
     cout<<"rt2.arr "<<rt2.arr<<endl;
 ​
     cout<<"end"<<endl;
 }

输出结果

 __default constructor
 reusable.arr 0x56521b946e70
 __move constructor
 reusable.arr 0
 duplicated.arr 0x56521b946e70
 rvalue--
 __default constructor
 createTest:0x7ffd092ea390
 rt1.arr 0x56521b94c0b0
 no rvalue--
 __default constructor
 createTest:0x7ffd092ea3c0
 createTest:0x7ffd092ea3c0
 rt2.arr 0x56521b950ee0
 end
 ..destructor normalVal
 ..destructor rval
 ..destructor duplicated
 ..destructor reusable

左值引用右值引用

 //左值引用和右值引用
 void foo(const int & i) { cout << "const int & " << i << endl; }
 void foo(int & i) {  cout << "int & " << i << endl; }
 void foo(int && i) { cout << "int && " << i << endl; }
 void foo(const int && i) { cout << "const int && " << i << endl; }
 void main(){
     int i = 2;
     foo(i);
     foo(2);
     foo([]()->const int && {return 2;}());
 }

完美转发

 /*在main当中调用relay,Test的临时对象作为一个右值传入relay,在relay当中又被转发给了func,那这时候转发
 给func的参数t也应当是一个右值。也就是说,我们希望:当relay的参数是右值的时候,func的参数也是右值;当
 relay的参数是左值的时候,func的参数也是左值。
 */
 class Test {   
     public:
     int * arr{nullptr};
     Test():arr(new int[5000]{1,2,3,4}) { 
         cout << "default constructor" << endl;
     }
     Test(const Test & t) {
         cout << "copy constructor" << endl;
         if (arr == nullptr) arr = new int[5000];
         copy(t.arr,t.arr+5000, arr);
     }
     Test(Test && t): arr(t.arr) {
         cout << "move constructor" << endl;
         t.arr = nullptr;
     }
     ~Test(){
         cout << "destructor" << endl;
         delete [] arr;
     }
 };
 ​
 template <typename T>
 void func(T t) {
     cout << "in func" << endl;
 }
 ​
 template <typename T>
 void relay(T&& t) {
     cout << "in relay" << endl;
     func(t);
 }
 //完美转发
 template <typename T>
 void relay1(T&& t) {
     cout << "in relay " << endl;
     func(std::forward<T>(t));
 }
 ​
 void main() {
     // relay(Test());
     // cout<<"end"<<endl;
 ​
     relay1(Test());
     cout<<"end"<<endl;
 ​
 }

标签:arr,cout,右值,int,lvalue,move,左值
来源: https://blog.csdn.net/qq_37869098/article/details/122398531

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有