c++ - 可以安全地使用复制/移动构造函数来实现复制/移动赋值运算符吗?

标签 c++ design-patterns c++11 constructor idioms

我认为下面的代码比 copy-and-swap 习惯用法要好。

这样就可以用两个宏来封装复制和移动赋值运算符的定义。换句话说,您可以避免在代码中显式定义它们。因此,您可以仅将注意力集中在 ctors 和 dtor 上。

这种方法有什么缺点吗?

class A
{
public:
    A() noexcept
        : _buf(new char[128])
    {}

    ~A() noexcept
    {
        if (_buf)
        {
            delete[] _buf;
            _buf = nullptr;
        }
    }

    A(const A& other) noexcept
        : A()
    {
        for (int i = 0; i < 128; ++i)
        {
            _buf[i] = other._buf[i];
        }
    }

    A(A&& other) noexcept
        : _buf(other._buf)
    {
        _buf = nullptr;
    }

    A& operator =(const A& other) noexcept
    {
        if (this != &other)
        {
            this->~A();
            new(this) A(other);
        }

        return *this;
    }

    A& operator =(A&& other) noexcept
    {
        if (this != &other)
        {
            this->~A();
            new(this) A(static_cast<A&&>(other));
        }

        return *this;
    }

private:
    char* _buf;
};

最佳答案

class A
{
public:
    A() noexcept
        : _buf(new char[128])
    {}

在上面,如果 new char[128] 抛出异常,A() 将调用 std::terminate()

    ~A() noexcept
    {
        if (_buf)
        {
            delete[] _buf;
            _buf = nullptr;
        }
    }

在上面,看起来不错。可以简化为:

    ~A() noexcept
    {
        delete[] _buf;
    }



    A(const A& other) noexcept
        : A()
    {
        for (int i = 0; i < 128; ++i)
        {
            _buf[i] = other._buf[i];
        }
    }

在上面,如果 new char[128] 抛出异常,将调用 std::terminate()。但除此之外还好。

    A(A&& other) noexcept
        : _buf(other._buf)
    {
        _buf = nullptr;
    }

在上面,看起来不错。

    A& operator =(const A& other) noexcept
    {
        if (this != &other)
        {
            this->~A();
            new(this) A(other);
        }

        return *this;
    }

在上面,通常我会说这是危险的。如果 new(this) A(other); 抛出怎么办?在这种情况下,它不会,因为如果它尝试这样做,程序就会终止。这是否安全取决于应用程序(终止对于 Ariane 5 效果不佳,但在更普通的应用程序中效果很好)。

    A& operator =(A&& other) noexcept
    {
        if (this != &other)
        {
            this->~A();
            new(this) A(static_cast<A&&>(other));
        }

        return *this;
    }

以上应该可以正常工作。虽然我不确定它是否优于下面的非分支版本,但性能相当。行为差异是以下版本不是 self 移动分配的空操作。然而,我相信自移动分配不必是空操作,因为后置条件之一声明结果值未指定(另一个后置条件声明它已指定,导致不可靠的矛盾)。

    A& operator =(A&& other) noexcept
    {
        delete [] _buf;
        _buf = nullptr;
        _buf = other._buf;
        other._buf = nullptr;
        return *this;
    }

关于c++ - 可以安全地使用复制/移动构造函数来实现复制/移动赋值运算符吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21768878/

相关文章:

c++ - 在自定义冒泡排序实现中比较迭代器崩溃程序而没有错误

c++ - 为什么我在我的 C++ 应用程序中放弃了 Bigtable?

c++ - 当 return 语句、逗号运算符、花括号初始化列表和 std::unique_ptr 组合在一起时

.net - 将大量静态数据写入代码而不是数据库?

design-patterns - 选择哪种设计模式

C++11 基于范围的 for 循环 : how to ignore value?

c++ - 实现一个动态数组(我可以访问不存在的位置)

c++ - 在 C++ 中创建一个文件,就像按右键->新建->新建文本文档一样

design-patterns - 是否有用于设计 DSL 的设计指南、教程或模式?

c++ - 编译时内存重新排序会导致死锁吗?