std::move

作者: arkliu | 来源:发表于2022-11-19 12:22 被阅读0次

    移动语义

    如果一个对象中有堆区资源,需要编写拷贝构造函数和赋值函数。

    深拷贝把对象中的堆区资源复制了一份,如果源对象(被拷贝的资源)是临时对象,拷贝完就没什么用了,这样会造成没有意义的申请资源和释放动作,如果能够直接使用源对象拥有的资源, 可以节省申请和释放时间,c++11新增的移动语义能够做到这一点。

    移动构造函数语法

    类名(类名&& 源对象) {.....}
    

    移动赋值函数语法

    类名& operator=类名&& 源对象) {.....}
    

    实现类的移动构造和移动赋值函数

    #include <iostream>
    #include <memory>
    #include<string>
    #include <cstring>
    using namespace std;
    
    class AA {
        public:
            int * m_a = nullptr; // 指向堆区资源的指针
    
            AA()=default;  // 启用默认构造函数
    
            void alloc() {
                m_a = new int(); // 分配内存
                memset(m_a, 0, sizeof(int)); // 初始化已分配的内存
            }
    
            AA(const AA &ra) { //拷贝构造函数
                cout << "调用了拷贝构造函数"<<endl;
                if (m_a == nullptr) alloc();
                memcpy(this->m_a, ra.m_a, sizeof(int)); //把数据从源对象拷贝过来
            }
    
            AA(AA&& ra) { //移动构造函数, 参数是右值引用
                cout << "调用了移动构造函数"<<endl;
                if (m_a != nullptr) delete m_a;  // 如果已经分配内存,先释放掉
                m_a = ra.m_a;      // 把资源从源对象中转移过来
                ra.m_a = nullptr;  //把源对象中的指针置空
            }
    
            AA& operator=(const AA& ra) { // 赋值函数
                cout << "调用了赋值函数"<<endl;
                if (this == &ra) return *this;
                if (m_a == nullptr) alloc();
                memcpy(this->m_a, ra.m_a, sizeof(int)); //把数据从源对象拷贝过来
                return *this;
            }
    
            // 移动赋值函数
            AA& operator=(AA&& ra) { 
                cout << "调用了移动赋值函数"<<endl;
                if (this == &ra) return *this;
                if (m_a != nullptr) delete m_a;  // 如果已经分配内存,先释放掉
                m_a = ra.m_a;      // 把资源从源对象中转移过来
                ra.m_a = nullptr;  //把源对象中的指针置空
                return *this;
            }
    
            ~AA() {
                if (m_a != nullptr)
                {
                    delete m_a;
                    m_a = nullptr;
                }
                
            }
    
    };
    
    int main() {
        // 如果形参是左值 就调用拷贝构造函数,如果形参是右值,就调用移动构造函数
        AA a1;  // 创建对象a1
        a1.alloc(); // 分配堆区资源
        *(a1.m_a) = 10;  //给堆区内存赋值
        cout << "a1.m_a=" << *a1.m_a << endl;
    
        AA a2 = a1; // 将调用拷贝构造函数
        cout << "a2.m_a=" << *a2.m_a << endl;
    
        AA a3;
        a3 = a1; // 将调用赋值构造函数
        cout << "a3.m_a=" << *a3.m_a << endl;
    
        cout << "----------------" << endl;
    
        // 返回aa类对象的lambda函数
        auto f =[]() {
            AA aa;
            aa.alloc();
            *(aa.m_a) = 55; 
            return aa;
        };
        AA a4 = f(); // lambda函数返回的临时对象是右值,将调用移动构造函数
        cout << "a4.m_a=" << *a4.m_a << endl;
    
        AA a5; 
        a5 = f(); // lambda函数返回的临时对象是右值,将调用移动赋值函数
        cout << "a5.m_a=" << *a5.m_a << endl;
        return 0;
    }
    
    • 对于一个左值,会调用拷贝构造函数,但是有些左值是局部变量,声明周期也很短,c++11提供了一个std::move()方法来将左值转义为右值,从而方便使用移动语义。
      其实就是告诉编译器\color{red}{虽然我是一个左值,但不要对我调用拷贝构造函数,而是要使用移动构造函数}

    • 如果没有提供移动构造/赋值函数, 只提供了拷贝构造/赋值函数,编译器找不到移动构造/赋值函数,就去寻找拷贝构造/赋值函数

    • c++11 中的所有容器,都实现了移动语义,避免对含有资源的对象,发生无谓的拷贝。

    • 移动语义,对拥有资源(内存,文件句柄) 的对象有用,如果是基本类型,使用移动语义没有意义。

    int main() {
        // 如果形参是左值 就调用拷贝构造函数,如果形参是右值,就调用移动构造函数
        AA a1;  // 创建对象a1
        a1.alloc(); // 分配堆区资源
        *(a1.m_a) = 10;  //给堆区内存赋值
        cout << "a1.m_a=" << *a1.m_a << endl;
    
        AA a2 = move(a1); // move后,a1左值被转义为右值,将调用移动构造函数
        cout << "a2.m_a=" << *a2.m_a << endl;
    
        AA a3;
        a3 = move(a1); // move后,a1左值被转义为右值,,将调用移动赋值函数
        cout << "a3.m_a=" << *a3.m_a << endl;
        return 0;
    }
    
    image.png

    相关文章

      网友评论

          本文标题:std::move

          本文链接:https://www.haomeiwen.com/subject/raqetdtx.html