编写自动赋值安全复制功能的正确方法是什么? C ++

问题描述 投票:2回答:2

我正在读一本关于c ++的书,在“复制控制”部分,作者教我们在课堂上写operator=告诉我们,当我们有一个使用动态的类时,我们必须确保该方法是安全的自我分配记忆。

所以,想象一下,我们有一个名为“Bank_Client”的类,其中使用std::string创建了new。这本书告诉我们这样做是为了避免自我分配的情况:

Bank_Client& Bank_Client::operator=(const Bank_Client &addres){
    std::string *temp = new std::string(*addres.name);
    delete name;
    name = temp;
    return *this;
}

所以,如果我这样做

Bank_Client bob("Bobinsky");
bob = bob;

该计划不会爆炸。但正当我认为temp变量浪费时间时,本书的作者向我们展示了另一种方法:

Bank_Client& Bank_Client::operator=(const Bank_Client &addres){
    if (this != &addres){
        delete name;
        name = new std::string(*addres.name);
    }
    return *this;
}

就像他读了我的想法一样。但是在那之后他告诉我们永远不要那样做,那就更好地通过另一种方式来做,但永远不解释原因。

为什么第一种方式更好?它比较慢,不是吗?这样做的最佳原因是什么?

怎么用assert检查没有自我分配? (因为我们真的不需要它)。然后用相应的NDEBUG去激活它,然后在检查中没有浪费时间。

c++ copy-constructor
2个回答
0
投票

你应该使用复制和交换。为此,您需要一个复制构造函数(如果您还想使用移动语义,也可能需要移动构造函数)。

class Bank_Client {
    // Note that swap is a free function:
    // This is important to allow it to be used along with std::swp
    friend void swap(Bank_Client& c1, Bank_Client& c2) noexcept {
        using std::swap;
        swap(c1.name, c2.name);
        // ...
    }
    // ...
};

// Note that we took the argument by value, not const reference
Bank_Client& Bank_Client::operator=(Bank_Client address) {
    // Will call the swap function we defined above
    swap(*this, adress);
    return *this;
}

现在,我们来看看客户端代码:

Bank_Client bob("Bobinsky");
// 1. Copy constructor is called to construct the `address` parameter of `operator=()`
// 2. We swap that newly created copy with the current content of bob
// 3. operator=() returns, the temporary copy is destroyed, everything is cleaned up!
bob = bob;

显然,你在进行自我赋值时会创建一个无用的副本,但它的优点是允许你在复制构造函数中重用逻辑(或移动构造函数)并且它是异常安全的:如果初始副本抛出异常,则不会执行任何操作。

另一个好处是你只需要一个operator=()实现来处理复制和移动语义(复制和交换以及移动和交换)。如果性能是一个大问题,你仍然可以有一个rvalue重载operator=(Bank_Client&&),以避免额外的移动(虽然我不鼓励它)。

最后,我还建议你尝试尽可能地依赖rule of 0,如上例所示,如果类的内容发生变化,你还必须相应地更新交换函数。


1
投票

如果对象是自我分配的,第一种方式会更慢。但是,自我分配很少见。在所有其他情况下,the additional if check from the second approach is a waste

也就是说,这两种方法都是实现拷贝赋值运算符的不良方法:两者都不是异常安全的。如果赋值在中途失败,则会在一些不一致的状态下留下半分配的对象。从复制构造函数中部分复制逻辑也很糟糕。相反,您应该使用the copy-and-swap idiom实现复制赋值运算符。

© www.soinside.com 2019 - 2024. All rights reserved.