拷贝与交换惯用法

拷贝与交换惯用法

用拷贝与交换惯用法实现赋值运算符,优点是简洁优雅,天然能处理自赋值且异常安全,瑕疵是会有额外的开销(包括额外的时间与空间成本,但大多数情况下都微不足道)。

class Widget
{
public:
    Widget() : p(new int) {}
    Widget(int a) : p(new int(a)) {}
    Widget(const Widget& theWidget);              
    Widget(Widget&& theWidget) noexcept;          
    ~Widget() { delete p; }
    int get() const { return *p; }
    const Widget& operator=( Widget theWidget);   
    void swap(Widget& lhs, Widget& rhs) noexcept;
    //...
private:
    int *p;
};

Widget::Widget(const Widget& theWidget) :          //拷贝构造函数
        p(new int(theWidget.get())) {}

Widget::Widget(Widget&& theWidget) noexcept :p(theWidget.p)  //移动构造函数
{
    theWidget.p = nullptr;
}
void Widget::swap(Widget& lhs, Widget& rhs) noexcept
{
    using std::swap;            //编译器会选择最适合的版本,标准库版本不会隐藏自定义版本
    swap(lhs.p, rhs.p);
}

const Widget& Widget::operator=( Widget theWidget)   //根据传入的值是右值还是左值调用对应的构造函数
{
    swap(*this, theWidget);    //与局部变量进行交换
    return *this;              
}

如果同时实现了拷贝构造与移动构造函数,则拷贝与交换实现的单一赋值运算符自动实现拷贝与移动两种赋值运算符的功能。

单独实现拷贝赋值运算符如下:

Widget& Widget::operator=(const Widget &theWidget)
{
    if (this != &theWidget)        //检查自赋值
    {
        auto temp = new int(theWidget.get());  //拷贝新内存
        delete p;                  //释放旧内存
        p = temp;                  //完成赋值
    }
    return *this;
}

其中条件判断是可选的,拷贝能正确处理自赋值且异常安全。原因是,在释放旧内存前拷贝了新内容,且拷贝过程是唯一可能发生异常的地方,一旦发生异常,程序立即终断,不会执行后续的释放操作。增加条件判断的好处是减少自赋值情况下拷贝的开销。

单独实现移动赋值运算符如下;

Widget& Widget::operator=(Widget &&theWidget) noexcept
{
    if (this != &theWidget)         //检查自赋值
    {
        p = theWidget.p;
        theWidget.p = nullptr;      //把移后源对象置于可析构状态
    }
    return *this;
}

因为移动操作不分配新的内存空间,所以不会抛出异常,只需检查自赋值即可,与上文提到的swap实现相比,优势在于开销较小。

总结一下,拷贝与交换惯用法天然处理自赋值且异常安全,在需要动态分配内存的时候(需要考虑自赋值和异常)推荐使用,并且额外增加的开销在绝大多数情况下都可忽略不计。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值