• C++11特性-右值与右值引用


     1.右值与右值引用

            左值:存储在内存中,有明确地址(可取地址)的数据

            右值:可以直接提供数据值的数据,不可取地址

            可以对表达式取地址(&)的就是左值,所有有名字的变量和对象都是左值;右值是匿名的

    1. //左值
    2. int num = 1;
    3. //左值引用
    4. int& a = num;
    5. //右值
    6. //右值引用
    7. int&& b = 5;
    8. //常量左值引用
    9. const int& c= num;
    10. //常量右值引用
    11. const int&& d = 2;
    12. //const int&& e = b;//error
    13. //const int&& f = d;//error

            无论左值引用还是右值引用,都必须初始化,都是取别名,都是为了提升效率;右值引用可以延长右值(临时变量)存活周期;左值引用为了避免指针或者值传递时的内存拷贝

            右值分为纯右值(数字、字符串、字面量常量、、lambda表达式、非引用返回的临时变量、运算表达式返回的临时变量)与将亡值(与右值引用有关的表达式,如T&&类型的函数返回值、std::move)

    1. class RightLifecycle
    2. {
    3. public:
    4. //浅拷贝
    5. RightLifecycle() :m_num(new int(100)){
    6. cout << "构造函数 :RightLifecycle " << endl;
    7. cout << "m_num的地址: " <<&m_num<< endl;
    8. }
    9. //拷贝构造函数,深拷贝
    10. RightLifecycle(const RightLifecycle &xx) :m_num(new int(*xx.m_num)) {
    11. cout << "拷贝构造函数 :RightLifecycle " << endl;
    12. }
    13. //移动构造函数(右值引用构造函数)-复用另一个对象的资源(堆内存)
    14. //m_num浅拷贝
    15. RightLifecycle(RightLifecycle&& xx) :m_num(xx.m_num) {
    16. xx.m_num = nullptr;
    17. cout << "移动构造函数 :RightLifecycle " << endl;
    18. }
    19. ~RightLifecycle() {
    20. cout << "析构函数 :RightLifecycle " << endl;
    21. delete m_num;
    22. }
    23. int* m_num;
    24. };
    25. RightLifecycle getObj() {
    26. //局部临时变量
    27. RightLifecycle t;
    28. return t;
    29. }
    30. RightLifecycle getObj1() {
    31. //临时匿名对象,将亡值
    32. return RightLifecycle();
    33. }
    34. RightLifecycle&& getObj2() {
    35. //临时匿名对象
    36. return RightLifecycle();
    37. }
    38. //调用
    39. //右侧对象是返回临时变量,移动构造函数才能被调用,否则调用拷贝构造函数
    40. //没有移动构造函数,会调用拷贝构造函数
    41. RightLifecycle objx = getObj();
    42. cout << "普通赋值 m_num的地址: " << objx.m_num << endl;
    43. cout << endl;
    44. RightLifecycle&& obj1x = getObj();
    45. cout << "右值引用 m_num的地址: " << obj1x.m_num << endl;
    46. cout << endl;
    47. //如果没有移动构造函数,使用右值引用的要求会更高些,
    48. //要求右侧对象是临时对象,同时不能去地址的对象,即返回一个临时匿名对象
    49. RightLifecycle&& obj2 = getObj1();
    50. cout << "右值引用 将亡值 m_num的地址: " << obj2.m_num << endl;
    51. cout << endl;
    52. RightLifecycle &&obj3 = getObj2();
    53. cout << "右值引用 将亡值 m_num的地址: " << obj3.m_num << endl;

            注意:大量申请资源的类应设计移动构造函数,同时提供构造函数,避免移动构造函数出错

                    模板参数的T &&与自动推导类型的auto &&表示未定的引用类型
                            通过右值推导的T&&与auto&&表示右值引用
                            除右值(左值、左值引用、常量左值引用、右值引用、常量右值引用)推导的T&&

                    与auto&&表示左值引用
                            const T &&表示右值引用

    1. template <typename T>
    2. void ftest_care1(T&& t) {}
    3. void ftest_care2(const T&& t) {}
    4. ftest_care1(10);//右值引用
    5. int x = 10;
    6. ftest_care1(x);//左值引用
    7. ftest_care2(x);//右值引用
    8. int x = 100;
    9. auto && a = x;//左值引用
    10. auto&& b = 3000;//右值引用
    11. const auto&& c = 6;//右值引用

            编译器会把已命名的右值引用视为左值,把未命名的优质引用视为右值

            右值引用在被推导或者传递后,对应的就是一个左值或者右值

    1. void test_transmit1(int& i) {
    2. cout << "左值引用" << i << endl;
    3. }
    4. void test_transmit1(int&& i) {
    5. cout << "右值引用" << i << endl;
    6. }
    7. void test_transmitT(int&& k) {
    8. test_transmit1(k);
    9. }
    10. //调用
    11. int a = 250;
    12. test_transmit1(a);
    13. test_transmit1(222);
    14. test_transmitT(777);

    2.资源转移move

            std::move()的作用是初始化右值引用,可以把一个左值转换为右值,是转移没有内存拷贝(与移动构造函数一样,具有移动的意思,把对象状态或者所有权移动到另一个对象)

            std::move等效与static_cast(左值)

    1. //资源转移,当ls不使用了,要使用ls2
    2. list<string> ls{ "sad","dsfs","wer" };
    3. list<string> ls1 = move(ls);
    4. //初始化右值引用
    5. int a = 99;
    6. int&& b = move(a);

    3.完美转发forward

            保证右值引用在参数传递时不会变为左值引用。

            forward(t);当t为左值引用时,t将会被转换成一个T类型左值,当t为不为左值时,t将会被转换成一个T类型右值

    1. template <typename TF>
    2. void printfforward(TF & tf) {
    3. cout<<"左值引用 : " << tf<< endl;
    4. }
    5. template <typename TF>
    6. void printfforward(TF&& tf) {
    7. cout << "右值引用 : " << tf << endl;
    8. }
    9. template <typename TF>
    10. void printfforwardT(TF&& tfT) {
    11. //右值引用传参时,根据情况看变为什么类型,这里是左值
    12. printfforward(tfT);
    13. printfforward(move(tfT));//左值变为右值
    14. printfforward(forward<TF>(tfT));//确保不会变换类型
    15. cout << endl;
    16. }
    17. //调用
    18. //forward<T>(t);
    19. //当t为左值引用时,t将会被转换成一个T类型左值
    20. //当t为不为左值时,t将会被转换成一个T类型右值
    21. printfforwardT(520);
    22. int num = 1000;
    23. printfforwardT(num);
    24. printfforwardT(forward<int>(num));//当t为不为左值时,t将会被转换成一个T类型右值
    25. printfforwardT(forward<int&>(num));//当t为左值引用时,t将会被转换成一个T类型左值
    26. printfforwardT(forward<int&&>(num));//当t为不为左值引用时,t将会被转换成一个T类型右值
  • 相关阅读:
    NFV中:DPDK与SR-IOV应用场景及性能对比
    【Flutter】底部导航BottomNavigationBar的使用
    使用Vue、ElementUI实现登录注册,配置axios全局设置,解决CORS跨域问题
    很迷茫要不要学习次世代建模,看完这8个要点,豁然开朗
    探索Web3前沿:革新性算力共享平台,重塑数字资源利用新时代
    Android 10.0 系统默认打开OEM解锁开关功能实现
    Linux 开源数据库Mysql-10-mysql集群一主一从GTID
    Debezium系列之:安装部署debezium2.0以上版本的详细步骤
    tomcat乱码解决
    基于Android车载系统模块资料
  • 原文地址:https://blog.csdn.net/weixin_44840658/article/details/128195347