经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
c++的左值(lvalue),右值(rvalue),移动语义(move),完美转发(forward)
来源:cnblogs  作者:xutopia  时间:2022/1/17 11:12:35  对本文有异议

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

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

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

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

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

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

右值引用:函数的返回值

int& 左值引用

int&& 右值引用

c++中无用拷贝的情况

  1. /*类里面 没有移动构造函数
  2. 这样就会使用 copy construct function,会导致大量无用的 memory copy。
  3. */
  4. class Test {
  5. public:
  6. string desc;
  7. int * arr{nullptr};
  8. Test():arr(new int[5000]{1,2,3,4}) {
  9. cout << "default constructor" << endl;
  10. }
  11. Test(const Test & t) {
  12. cout << "copy constructor" << endl;
  13. if (arr == nullptr) arr = new int[5000];
  14. copy(t.arr,t.arr+5000, arr);
  15. }
  16. ~Test(){
  17. cout << "destructor " << desc << endl;
  18. delete [] arr;
  19. }
  20. };
  21. Test createTest() {
  22. return Test();
  23. }
  24. int main(){
  25. Test reusable;
  26. reusable.desc = "reusable";
  27. Test duplicated(reusable);
  28. duplicated.desc = "duplicated";
  29. Test t(createTest());
  30. t.desc = "t";
  31. cout<<"end"<<endl;
  32. }

运行结果

  1. default constructor
  2. copy constructor
  3. default constructor
  4. end
  5. destructor t
  6. destructor duplicated
  7. destructor reusable

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

  1. /*使用移动 construct function,避免无用的memory copy。
  2. */
  3. class Test {
  4. public:
  5. string desc;
  6. int * arr{nullptr};
  7. Test():arr(new int[5000]{1,2,3,4}) {
  8. cout << "__default constructor" << endl;
  9. }
  10. Test(const Test & t) {
  11. cout << "__copy constructor" << endl;
  12. if (arr == nullptr) arr = new int[5000]; //在这里要将 t.arr 置为空,因为经过move之后,我们认为不在使用这个值了,避免在新的对象中把指针释放后,原来的对象中存在野指针的现象
  13. copy(t.arr,t.arr+5000, arr);
  14. }
  15. Test(Test && t): arr(t.arr) {
  16. cout << "__move constructor" << endl;
  17. t.arr = nullptr;
  18. }
  19. ~Test(){
  20. cout << "..destructor " << desc << endl;
  21. delete [] arr;
  22. }
  23. };
  24. Test createTest(string str) {
  25. Test rt;
  26. rt.desc = str;
  27. cout<<"createTest:"<<&rt<<endl;
  28. return rt;
  29. }
  30. void main(){
  31. Test reusable;
  32. reusable.desc = "reusable";
  33. cout<<"reusable.arr "<<reusable.arr<<endl;
  34. Test duplicated(std::move(reusable));
  35. duplicated.desc = "duplicated";
  36. cout<<"reusable.arr "<<reusable.arr<<endl;
  37. cout<<"duplicated.arr "<<duplicated.arr<<endl;
  38. cout<<"rvalue--"<<endl;
  39. Test&& rt1 = createTest("rval"); //使用右值引用接收
  40. cout<<"rt1.arr "<<rt1.arr<<endl;
  41. cout<<"no rvalue--"<<endl;
  42. Test rt2 = createTest("normalVal"); //不使用右值引用接收,可以看到这里比使用右值引用接收 多了一次构造和析构(createTest中的临时对象)
  43. cout<<"createTest:"<<&rt2<<endl; //尴尬,其实这里编译器已经做了优化了,可以看到第地址一样
  44. cout<<"rt2.arr "<<rt2.arr<<endl;
  45. cout<<"end"<<endl;
  46. }

输出结果

  1. __default constructor
  2. reusable.arr 0x56521b946e70
  3. __move constructor
  4. reusable.arr 0
  5. duplicated.arr 0x56521b946e70
  6. rvalue--
  7. __default constructor
  8. createTest:0x7ffd092ea390
  9. rt1.arr 0x56521b94c0b0
  10. no rvalue--
  11. __default constructor
  12. createTest:0x7ffd092ea3c0
  13. createTest:0x7ffd092ea3c0
  14. rt2.arr 0x56521b950ee0
  15. end
  16. ..destructor normalVal
  17. ..destructor rval
  18. ..destructor duplicated
  19. ..destructor reusable

左值引用右值引用

  1. //左值引用和右值引用
  2. void foo(const int & i) { cout << "const int & " << i << endl; }
  3. void foo(int & i) { cout << "int & " << i << endl; }
  4. void foo(int && i) { cout << "int && " << i << endl; }
  5. void foo(const int && i) { cout << "const int && " << i << endl; }
  6. void main(){
  7. int i = 2;
  8. foo(i);
  9. foo(2);
  10. foo([]()->const int && {return 2;}());
  11. }

完美转发

  1. /*在main当中调用relay,Test的临时对象作为一个右值传入relay,在relay当中又被转发给了func,那这时候转发
  2. 给func的参数t也应当是一个右值。也就是说,我们希望:当relay的参数是右值的时候,func的参数也是右值;当
  3. relay的参数是左值的时候,func的参数也是左值。
  4. */
  5. class Test {
  6. public:
  7. int * arr{nullptr};
  8. Test():arr(new int[5000]{1,2,3,4}) {
  9. cout << "default constructor" << endl;
  10. }
  11. Test(const Test & t) {
  12. cout << "copy constructor" << endl;
  13. if (arr == nullptr) arr = new int[5000];
  14. copy(t.arr,t.arr+5000, arr);
  15. }
  16. Test(Test && t): arr(t.arr) {
  17. cout << "move constructor" << endl;
  18. t.arr = nullptr;
  19. }
  20. ~Test(){
  21. cout << "destructor" << endl;
  22. delete [] arr;
  23. }
  24. };
  25. template <typename T>
  26. void func(T t) {
  27. cout << "in func" << endl;
  28. }
  29. template <typename T>
  30. void relay(T&& t) {
  31. cout << "in relay" << endl;
  32. func(t);
  33. }
  34. //完美转发
  35. template <typename T>
  36. void relay1(T&& t) {
  37. cout << "in relay " << endl;
  38. func(std::forward<T>(t));
  39. }
  40. void main() {
  41. // relay(Test());
  42. // cout<<"end"<<endl;
  43. relay1(Test());
  44. cout<<"end"<<endl;
  45. }

更多编程资料见公众号 xutopia77

原文链接:http://www.cnblogs.com/xutopia/p/15782047.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号