zoukankan      html  css  js  c++  java
  • C++11新特性之右值引用(&&)、移动语义(move)、完美转换(forward)

    1. 右值引用

      个人认为右值引用的目的主要是为了是减少内存拷贝,优化性能。

      比如下面的代码:

    String Fun()
    {
        String str = "hello world";
        return str;
    }

      str为临时对象,然后调用Stringd的拷贝构造函数,将临时对象的值赋值给String,这种拷贝是完全没有必要的,如果堆内存很大,那么这个拷贝构造的代价会很大,带来了额外的性能损耗

      为了避免链式对象的拷贝构造,我们可以使用右值引用拷贝的方式来实现:

    MyString& operator=(MyString&& other)
        {
            cout << "MyString& operator=(const MyString&& other)" << endl;
            if (this != &other)
            {
                m_nLen = other.m_nLen;
                m_pData = other.m_pData;
                other.m_pData = NULL;
            }
    
            return *this;
        }

      上面的代码只是进行了指针权限的转移,而没有额外的性能消耗。

    1.1 使用右值引用实现MyString类

    #include "stdio.h"
    #include <string>
    #include <iostream>
    using namespace std;
    
    class MyString
    {
    public:
        MyString() :m_pData(NULL), m_nLen(0)
        {
            cout << "MyString()" << endl;
        }
        MyString(const char *pStr)   // 允许隐式转换
        {
            cout << "MyString(const char *pStr)" << endl;
            m_nLen = strlen(pStr);
            CopyData(pStr);
        }
        MyString(const MyString& other)
        {
            cout << "MyString(const MyString& other)" << endl;
            if (!other.m_pData)
            {
                m_nLen = other.m_nLen;
                DeleteData();
                CopyData(other.m_pData);
            }
        }
        MyString& operator=(const MyString& other)
        {
            cout << "MyString& operator=(const MyString& other)" << endl;
            if (this != &other)
            {
                m_nLen = other.m_nLen;
                DeleteData();
                CopyData(other.m_pData);
            }
    
            return *this;
        }
    
        MyString(MyString&& other)
        {
            cout << "MyString(MyString&& other)" << endl;
            m_nLen = other.m_nLen;
            m_pData = other.m_pData;
            other.m_pData = NULL;
        }
    
        MyString& operator=(MyString&& other)
        {
            cout << "MyString& operator=(const MyString&& other)" << endl;
            if (this != &other)
            {
                m_nLen = other.m_nLen;
                m_pData = other.m_pData;
                other.m_pData = NULL;
            }
    
            return *this;
        }
    
        ~MyString()
        {
            DeleteData();
        }
    
    private:
        void CopyData(const char *pData)
        {
            if (pData)
            {
                m_pData = new char[m_nLen + 1];
                memcpy(m_pData, pData, m_nLen);
                m_pData[m_nLen] = '';
            }
        }
    
        void DeleteData()
        {
            if (m_pData != NULL)
            {
                delete[] m_pData;
                m_pData = NULL;
            }
        }
    
    private:
        char *m_pData;
        size_t m_nLen;
    };
    
    MyString Fun()
    {
        MyString str = "hello world";
        return str;
    }
    void main()
    {
        MyString str1 = "hello";
        MyString str2(str1);
        MyString str3 = Fun();
    }

    1.2 右值引用总结

    C++11中引入了右值引用和移动语义,可以避免无谓的复制,提高了程序的性能,右值引用标记为T&&。

    (1)左值和右值是独立于它们的类型,右值引用类型可能是左值也可能是右值

    (2)auto&&或函数参数类型的自动推导的T&&是一个未定的引用类型,它可能是左值引用,也可能是右值引用,取决于初始化的值类型

    (3)所有的右值引用叠加到右值引用上仍然是一个右值引用,其它引用叠加都为坐值引用,当T&&为模版参数时,输入左值,它会变为左值引用,输入右值则变为具名的右值引用

    (4)编译器会将已命名的右值引用视为左值,而将未命名的右值视为右值

    2. move语义

      我们知道移动语义是通过右值引用来匹配临时值的,那么,普通的左值是否也能借组移动语义来优化性能呢?C++11为了解决这个问题,提供了std::move()方法来将左值转换为右值,从而方便应用移动语义。move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转义,没有内存拷贝。

    MyString str1 = "hello";
    MyString str2(str1);
    MyString str3 = Fun();
    MyString str4 = move(str2);

    3. forward

      forward将左值转换为右值:

    MyString str1 = "hello";
    MyString str2(str1);
    MyString str3 = Fun();
    MyString str4 = move(str2);
    MyString str5(forward<MyString>(str3));

    4. 综合示例

    #include "stdio.h"
    #include<iostream>
    
    #include<vector>
    using namespace std;
    
    class A
    {
    public:
        A() :m_ptr(NULL), m_nSize(0){}
        A(int *ptr, int nSize)
        {
            m_nSize = nSize;
            m_ptr = new int[nSize];
            if (m_ptr)
            {
                memcpy(m_ptr, ptr, sizeof(sizeof(int) * nSize));
            }
        }
        A(const A& other)   // 拷贝构造函数实现深拷贝
        {
            m_nSize = other.m_nSize;
            if (other.m_ptr)
            {
                delete[] m_ptr;
                m_ptr = new int[m_nSize];
                memcpy(m_ptr, other.m_ptr, sizeof(sizeof(int)* m_nSize));
            }
            else
            {
                m_ptr = NULL;
            }
            cout << "A(const int &i)" << endl;
        }
    
        // 右值应用构造函数
        A(A &&other)    
        {
            m_ptr = NULL;
            m_nSize = other.m_nSize;
            if (other.m_ptr)
            {
                m_ptr = move(other.m_ptr);  // 移动语义
                other.m_ptr = NULL;
            }
        }
    
        ~A()
        {
            if (m_ptr)
            {
                delete[] m_ptr;
                m_ptr = NULL;
            }
        }
    
        void deleteptr()
        {
            if (m_ptr)
            {
                delete[] m_ptr;
                m_ptr = NULL;
            }
        }
    
        int *m_ptr;
        int m_nSize;
    };
    
    void main()
    {
        int arr[] = { 1, 2, 3 };
        A a(arr, sizeof(arr)/sizeof(arr[0]));
        cout << "m_ptr in a Addr: 0x" << a.m_ptr << endl;
        A b(a);
        cout << "m_ptr in b Addr: 0x" << b.m_ptr << endl;
        b.deleteptr();
        A c(std::forward<A>(a));   // 完美转换
        cout << "m_ptr in c Addr: 0x" << c.m_ptr << endl;
        c.deleteptr();
    
        vector<int> vect{ 1, 2, 3, 4, 5 };
        cout << "before move vect size: " << vect.size() << endl;
    
        vector<int> vect1 = move(vect);
        cout << "after move vect size: " << vect.size() << endl;
        cout << "new vect1 size: " << vect1.size() << endl;
    }

  • 相关阅读:
    git push 出现 you are not allowed to upload merges 错误提示
    构建React-app应用时create-react-app卡住超慢的解决办法<转>
    防抖与节流函数<转>
    this全面解析<转>
    正确的安装和使用nvm(mac)<转>
    TypeScript如何添加自定义d.ts文件(转)
    为什么angular library的build不能将assets静态资源打包进去(转)
    Error: EACCES: permission denied, access '/usr/local/lib/node_modules'
    linux命令新建文件
    mac系统终端sudo免输入密码技能get
  • 原文地址:https://www.cnblogs.com/xiaobingqianrui/p/9064260.html
Copyright © 2011-2022 走看看