复制和交换惯用语是否将成为C ++ 11中的复制和移动惯用语? [英] Should the Copy-and-Swap Idiom become the Copy-and-Move Idiom in C++11?

查看:203
本文介绍了复制和交换惯用语是否将成为C ++ 11中的复制和移动惯用语?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此答案中所述,复制和交换惯用语实现如下:

As explained in this answer, the copy-and-swap idiom is implemented as follows:

class MyClass
{
private:
    BigClass data;
    UnmovableClass *dataPtr;

public:
    MyClass()
      : data(), dataPtr(new UnmovableClass) { }
    MyClass(const MyClass& other)
      : data(other.data), dataPtr(new UnmovableClass(*other.dataPtr)) { }
    MyClass(MyClass&& other)
      : data(std::move(other.data)), dataPtr(other.dataPtr)
    { other.dataPtr= nullptr; }

    ~MyClass() { delete dataPtr; }

    friend void swap(MyClass& first, MyClass& second)
    {
        using std::swap;
        swap(first.data, other.data);
        swap(first.dataPtr, other.dataPtr);
    }

    MyClass& operator=(MyClass other)
    {
        swap(*this, other);
        return *this;
    }
};

通过将MyClass的值作为operator =的参数,可以通过副本构造函数或移动构造函数。然后,您可以安全地从参数中提取数据。这可以防止代码重复和协助异常安全。

By having a value of MyClass as parameter for operator=, the parameter can be constructed by either the copy constructor or the move constructor. You can then safely extract the data from the parameter. This prevents code duplication and assists in exception safety.

回答提到你可以交换或移动临时变量。它主要讨论交换。但是,如果未由编译器优化,则交换包括三个移动操作,在更复杂的情况下执行额外的额外工作。

The answer mentions you can either swap or move the variables in the temporary. It primarily discusses swapping. However, a swap, if not optimised by the compiler, involves three move operations, and in more complex cases does additional extra work. When all you want, is to move the temporary into the assigned-to object.

考虑这个更复杂的例子,涉及到观察者模式。在这个例子中,我手动写了赋值运算符代码。重点是移动构造函数,赋值运算符和交换方法:

Consider this more complex example, involving the observer pattern. In this example, I've written the assignment operator code manually. Emphasis is on the move constructor, assignment operator and swap method:

class MyClass : Observable::IObserver
{
private:
    std::shared_ptr<Observable> observable;

public:
    MyClass(std::shared_ptr<Observable> observable) : observable(observable){ observable->registerObserver(*this); }
    MyClass(const MyClass& other) : observable(other.observable) { observable.registerObserver(*this); }
    ~MyClass() { if(observable != nullptr) { observable->unregisterObserver(*this); }}

    MyClass(MyClass&& other) : observable(std::move(other.observable))
    {
        observable->unregisterObserver(other);
        other.observable.reset(nullptr);
        observable->registerObserver(*this);
    }

    friend void swap(MyClass& first, MyClass& second)
    {
        //Checks for nullptr and same observable omitted
            using std::swap;
            swap(first.observable, second.observable);

            second.observable->unregisterObserver(first);
            first.observable->registerObserver(first);
            first.observable->unregisterObserver(second);
            second.observable->registerObserver(second);
    }

    MyClass& operator=(MyClass other)
    {
        observable->unregisterObserver(*this);
        observable = std::move(other.observable);

        observable->unregisterObserver(other);
        other.observable.reset(nullptr);
        observable->registerObserver(*this);
    }
}

很明显,此处手动复制的代码部分书写赋值运算符与移动构造函数的运算符相同。您可以在赋值运算符中执行交换,并且行为是正确的,但它可能执行更多的移动,并执行额外的注册(在交换)和注销(在析构函数中)。

Clearly, the duplicated part of the code in this manually written assignment operator is identical to that of the move constructor. You could perform a swap in the assignment operator and the behaviour would be right, but it would potentially perform more moves and perform an extra registration (in the swap) and unregistration (in the destructor).

如果不使用移动构造函数的代码,这样会更有意义吗?

Wouldn't it make much more sense to reuse the move constructor's code in stead?

private:
    void performMoveActions(MyClass&& other)
    {
        observable->unregisterObserver(other);
        other.observable.reset(nullptr);
        observable->registerObserver(*this);
    }

public:
    MyClass(MyClass&& other) : observable(std::move(other.observable))
    {
        performMoveActions(other);
    }

    MyClass& operator=(MyClass other)
    {
        observable->unregisterObserver(*this);
        observable = std::move(other.observable);

        performMoveActions(other);
    }



看起来对我来说这种方法从来不逊于交换方法。我是否正在认为复制和交换惯用法会好于C ++ 11中的复制和移动习惯,或者我错过了重要的东西?

It looks to me like this approach is never inferior to the swap approach. Am I right in thinking that the copy-and-swap idiom would be better off as the copy-and-move idiom in C++11, or did I miss something important?

推荐答案

给每个特别成员一个温柔的爱护,应该尽量默认他们:

Give each special member the tender loving care it deserves, and try to default them as much as possible:

class MyClass
{
private:
    BigClass data;
    std::unique_ptr<UnmovableClass> dataPtr;

public:
    MyClass() = default;
    ~MyClass() = default;
    MyClass(const MyClass& other)
        : data(other.data)
        , dataPtr(other.dataPtr ? new UnmovableClass(*other.dataPtr)
                                : nullptr)
        { }
    MyClass& operator=(const MyClass& other)
    {
        if (this != &other)
        {
            data = other.data;
            dataPtr.reset(other.dataPtr ? new UnmovableClass(*other.dataPtr)
                                        : nullptr);
        }
        return *this;
    }
    MyClass(MyClass&&) = default;
    MyClass& operator=(MyClass&&) = default;

    friend void swap(MyClass& first, MyClass& second)
    {
        using std::swap;
        swap(first.data, second.data);
        swap(first.dataPtr, second.dataPtr);
    }
};

如果需要,析构函数可以隐式地默认。

The destructor could be implicitly defaulted above if desired. Everything else needs to be explicitly defined or defaulted for this example.

参考: http://accu.org/content/conf2014/Howard_Hinnant_Accu_2014.pdf

复制/交换惯用语可能会对您造成损失(见幻灯片) )。例如,为什么高性能/常常使用的std ::类型如 std :: vector std :: string 不要使用复制/交换?性能差是原因。如果 BigClass 包含任何 std :: vector s或 std :: string s(似乎可能),你最好的打赌是从你的特别成员叫他们的特别成员。以上是如何做到的。

The copy/swap idiom will likely cost you performance (see the slides). For example ever wonder why high performance / often used std::types like std::vector and std::string don't use copy/swap? Poor performance is the reason. If BigClass contains any std::vectors or std::strings (which seems likely), your best bet is to call their special members from your special members. The above is how to do that.

如果你需要强大的异常安全性的任务,请参阅幻灯片中除了性能提供(搜索strong_assign )。

If you need strong exception safety on the assignment, see the slides for how to offer that in addition to performance (search for "strong_assign").

这篇关于复制和交换惯用语是否将成为C ++ 11中的复制和移动惯用语?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆