C++日记——Day19:对象移动、移动构造函数、移动赋值运算符

对象移动的概念

把临时对象中一些有用的数据留出来给之后的对象继续使用,提升程序性能。

 

移动构造函数和移动赋值运算符概念

移动构造函数:进一步提高程序效率。

说明:

1、A移动给B,那么对象A我们就不能再使用了。

2、并不是把内存中的地址从一个地址移动到另外一个地址,只是所有者变更了。

 

移动构造函数:

Time::Time(const Time &&tmpTime) //右值引用

 

移动构造函数和移动赋值运算符应该完成的功能

1、完成必要的内存移动,斩断元对象和内存的关系。

2、确保移动后源对象处于一种“即便被销毁也没有什么问题”的状态。B<-A,确保不在使用A,而是应该使用B。

 

移动构造函数演示

class B {
public:
    int m_i;

    B(int i = 0):m_i(i) { };
    B(B &b):m_i(b.m_i) { };
    virtual ~B() {};
};


class A {
public:
    A():m_pb(new B()) { };
    A(A &a): m_pb(new B(*(a.m_pb))) { };
    virtual ~A(){
        delete m_pb;
    }
    A(A &&tmpA) :m_pb(tmpA.m_pb)  //类A的移动构造函数,
    {                  
        tmpA.m_pb = nullptr; //切记!!!移动后要打断原来的指针
    }


private:
    B *m_pb;
};


static A getA()  //static作用:当前函数只在这个文件中有效
{
    A a;
    return a; //如果类A有移动构造函数,那么会调用移动构造函数
}

int main(){

    A a = getA(); //1个拷贝构造函数,一个移动构造函数,1个析构函数
    A a1(a);    //调用个拷贝构造函数
}

 

class A{
public:
    A(A &&tmpA) noexcept ;  //类A的移动构造函数,
    
};


A:: A(A &&tmpA) noexcept :m_pb(tmpA.m_pb)
{                  
    tmpA.m_pb = nullptr; //切记!!!移动后要打断原来的指针
}

noexpect :通知标准库,我们这个移动构造函数不抛出任何异常(提高编译器的工作效率)

一般移动构造函数都要加 noexcept,声明和定义处都要加。

int main(){
    A a = getA(); //1个拷贝构造函数,一个移动构造函数,1个析构函数
    A a1(std::move(a));    //通过std::move将a从左值转换成右值后就会触发A的移动构造函数
    A &&a2(std::move(a));   //这里没有建立新对象,根本不会调用移动构造函数
                            //这个效果等同于把对象a有了一个新的别名a2
}
A &&c = getA(); //从getA()返回的临时对象,被c接管了。

 

移动赋值运算符

A &operator=(const A &src){    //拷贝赋值运算符
    if (this == &src)
        return *this;

    delete m_pb;
    m_pb = new B(*(src.m_pb));
    return *this;
}


A &operator=(A &&src) noexcept{   //移动赋值运算符
    if (this == &src)
        return *this;

    delete m_pb;
    m_pb = src.m_pb;  //对方的内存直接拿过来
    src.m_pb = nullptr; //斩断源
    return *this;
}


int main(){
    A a = getA();
    A a2;
    a2 = std::move(a);//调用移动赋值运算符    

}

 

 五:合成的移动操作

某些条件下,编译器能合成移动构造函数,移动赋值运算符

1、有自己的拷贝构造函数,自己的拷贝赋值运算符或者自己的析构,那么编译器就不会为它合成移动构造函数和移动赋值运算符。

2、如果我们没有自己的移动构造函数和移动赋值运算符,那么系统会调用我们自己写的拷贝构造函数和拷贝赋值运算符来代替

3、只有一个类没定义任何自己版本的拷贝构造成员(没有拷贝构造函数也没有拷贝赋值运算符),且类的每个非静态成员都可以移动时编译器才会为该类合成移动构造函数或者移动赋值运算符

什么叫成员可以移动?

a、内置类型是可以移动的

b、类类型的成员,则这个类要有对应的移动操作的相关函数,就可以移动。(比如移动构造函数和移动赋值运算符)

此时编译器就会为我们生成移动构造函数和移动赋值运算符。

 

总结:

1、尽量给类增加移动构造函数和移动赋值运算符;

2、noexcept 尽量要加在移动构造函数和移动赋值运算符处;

3、该给nullptr的就要给nullptr让被移动对象随时可以处于一种可以被析构的状态;

4、没有移动会调用拷贝代替。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值