C++(11):移动构造函数
Published on 2022-07-13 14:45 in 暂未分类 with 萧海~

C++(11):移动构造函数

    传统上C++是通过拷贝构造函数完成通过一个对象初始化另一个对象:

    class A{
    public:
    A()
    {
    m_i = new int(1);
    cout<<"Construct A, this addr:"<<this<<", m_i addr:"<<m_i<<endl;
    }
    A(const A &a):m_i(new int(*a.m_i))
    {
    cout<<"Copy Construct A, ori addr:"<<&a<<" ori m_i addr:"<<a.m_i<<" *m_i="<<*a.m_i<<", this addr:"<<this<<" m_i addr:"<<m_i<<" *a.m_i="<<*a.m_i<<endl;
    }
    ~A()
    {
    free(m_i);
    m_i = 0;
    cout<<"Destruct A, this addr:"<<this<<" m_i addr:"<<m_i<<endl;
    }
    int *m_i;
    };
    A getA()
    {
    return A();
    }
    int main(){
    A a3 = getA();
    cout<<"a3 addr:"<<&a3<<" a3.m_i addr:"<<a3.m_i<<" *a3.m_i="<<*a3.m_i<<endl;
    return 0;
    }
    运行程序输出:
    //return A()构造的临时对象,记作对象a1
    Construct A, this addr:0x63fdb8, m_i addr:0x6b6f90
    //getA返回的临时对象,该对象是通过拷贝构造函数复制对象a1得到的,记作对象a2
    Copy Construct A, ori addr:0x63fdb8 ori m_i addr:0x6b6f90 *m_i=1, this addr:0x63fe08 m_i addr:0x6b6fb0 *a.m_i=1
    //对象a1析构
    Destruct A, this addr:0x63fdb8 m_i addr:0
    //main函数中的A a3对象,该对象是通过拷贝构造函数复制对象a2得到的
    Copy Construct A, ori addr:0x63fe08 ori m_i addr:0x6b6fb0 *m_i=1, this addr:0x63fe00 m_i addr:0x6b6f90 *a.m_i=1
    //对象a2析构
    Destruct A, this addr:0x63fe08 m_i addr:0
    //main中输出a3的信息
    a3 addr:0x63fe00 a3.m_i addr:0x6b6f90 *a3.m_i=1
    //对象a3析构
    Destruct A, this addr:0x63fe00 m_i addr:0

    对象a2是调用拷贝构造函数,复制a1构造的,a2与a1的m_i拥有各自的内存地址,地址里的数值都是1

    对象a3是调用拷贝构造函数,复制a2构造的,a3与a2的m_i拥有各自的内存地址,地址里的数值都是1

    对象a1,a2,a3会各自分配自己的m_i指向各自的内存,对于像a1和a2这样的临时对象,不仅仅需要构造他们,还需要为他们分配和释放内存,而内存的意义就是为了让后续的对象拷贝构造,这对于分配了大量内存的对象来说,是非常低效的。

    针对这种情况C++11引入了移动构造函数的概念,可以通过它将临时对象的内存转移给后续对象,减少不必要的内存分配和释放:

    #include <iostream>
    using namespace std;
    class A{
    public:
    A()
    {
    m_i = new int(1);
    cout<<"Construct A, this addr:"<<this<<", m_i addr:"<<m_i<<endl;
    }
    A(const A &a):m_i(new int(*a.m_i))
    {
    cout<<"Copy Construct A, ori addr:"<<&a<<" ori m_i addr:"<<a.m_i<<" *m_i="<<*a.m_i<<", this addr:"<<this<<" m_i addr:"<<m_i<<" *a.m_i="<<*a.m_i<<endl;
    }
    A(A&& a):m_i(a.m_i)//移动构造函数接受一个所谓的“右值引用”的参数,完成资源的转移
    {
    cout<<"Move Construct A, ori addr:"<<&a<<" ori m_i addr:"<<a.m_i<<" *m_i="<<*a.m_i<<", this addr:"<<this<<" m_i addr:"<<m_i<<" *a.m_i="<<*a.m_i<<endl;
    a.m_i = 0;
    }
    ~A()
    {
    cout<<"Destruct A, this addr:"<<this<<" m_i addr:"<<m_i<<endl;
    if(m_i)
    {
    free(m_i);
    m_i = 0;
    }
    }
    int *m_i;
    };
    A getA()
    {
    return A();
    }
    int main(){
    A a3 = getA();
    cout<<"a3 addr:"<<&a3<<" a3.m_i addr:"<<a3.m_i<<" *a3.m_i="<<*a3.m_i<<endl;
    return 0;
    }
    运行程序输出:
    //return A()构造的临时对象,记作对象a1
    Construct A, this addr:0x63fdb8, m_i addr:0x776f90
    //通过移动构造函数,将对象a1的m_i转移给getA的返回值临时对象a2;a2的m_i与a1的m_i指向相同的内存地址
    Move Construct A, ori addr:0x63fdb8 ori m_i addr:0x776f90 *m_i=1, this addr:0x63fe08 m_i addr:0x776f90 *a.m_i=1
    //对象a1析构,由于其资源m_i已转移给对象a2,因此无需释放内存
    Destruct A, this addr:0x63fdb8 m_i addr:0
    //通过移动构造函数,将对象a2的m_i转移给对象a3;a3的m_i与a2的m_i指向相同的内存地址
    Move Construct A, ori addr:0x63fe08 ori m_i addr:0x776f90 *m_i=1, this addr:0x63fe00 m_i addr:0x776f90 *a.m_i=1
    //对象a2析构,由于其资源m_i已转移给对象a3,因此无需释放内存
    Destruct A, this addr:0x63fe08 m_i addr:0
    //输出对象a3的信息, a3的m_i指向了最原始的对象a1分配的m_i
    a3 addr:0x63fe00 a3.m_i addr:0x776f90 *a3.m_i=1
    //对象a3析构,释放内存
    Destruct A, this addr:0x63fe00 m_i addr:0x776f90

    可以看到通过移动构造函数,临时对象的资源可以进行转移,从而减少了不必要的内存分配和释放。

    需要注意的是:

    移动构造函数有一个触发条件,即:当一个对象的构造是基于另一个临时匿名对象时,才能触发。

    int main(){
    A a3 = getA(); //a3是通过临时对象进行构造,可以触发移动构造函数
    A a4 = a3; //a4是通过具名对象a3构造,因此是通过拷贝构造函数来构造
    return 0;
    }
    posted @   萧海~  阅读(276)  评论(0编辑  收藏  举报
    相关博文:
    阅读排行:
    · winform 绘制太阳,地球,月球 运作规律
    · AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
    · 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
    · 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
    · 上周热点回顾(3.3-3.9)
    点击右上角即可分享
    微信分享提示
    电磁波切换