复制和交换基类和派生类

gar*_*ese 5 c++ constructor base-class derived-class c++11

我最近阅读了有关复制和交换的内容,现在我正在尝试在基类和派生类中实现ctors.我的base和派生类中都有四个构造函数,但是我不确定如何实现派生类的赋值运算符.

explicit Base(int i) : m_i{i} {}
Base(const Base & other) : m_i{other.m_i}
Base(Base && other) : Base(0) { swap(*this, other); }
Base & operator=(Base other) { swap(*this, other); return *this; }
friend void swap(Base & a, Base & b) noexcept {
    using std::swap;
    swap(a.m_i, b.m_i);
}

explicit Derived(int j) : Base(42), m_j(j) {}
Derived(const Derived & other) : Derived(other.m_j) {}
Derived(Derived && other) : Derived(other.m_j) { swap(*this, other); }
Derived & operator=(Derived other) { /*???*/ }
friend void swap(Derived & a, Derived & b) noexcept {
    using std::swap;
    swap(a.m_j, b.m_j);
}
Run Code Online (Sandbox Code Playgroud)

How*_*ant 6

考虑= default尽可能多地使用.如果我们谈论公共继承,你真的需要一个虚拟析构函数.

以下是Base使用复制/交换样式的外观:

class Base
{
    int m_i;
public:
    virtual ~Base() = default;
    Base(const Base& other) = default;
    Base& operator=(Base other) noexcept
    {
        swap(*this, other);
        return *this;
    }
    Base(Base&& other) noexcept
        : Base(0)
    {
        swap(*this, other);
    }

    explicit Base(int i) noexcept
        : m_i{i}
        {}

    friend void swap(Base& a, Base& b) noexcept
    {
        using std::swap;
        swap(a.m_i, b.m_i);
    }
};
Run Code Online (Sandbox Code Playgroud)

与你所拥有的唯一区别是我添加了虚拟析构函数,并用于= default复制构造函数.

现在Derived:

class Derived
    : public Base
{
    int m_j;
public:
    Derived(const Derived& other) = default;
    Derived& operator=(Derived other) noexcept
    {
        swap(*this, other);
        return *this;
    }
    Derived(Derived&& other) noexcept
        : Derived(0)
    {
        swap(*this, other);
    }

    explicit Derived(int j) noexcept
        : Base(42)
        , m_j{j}
        {}

    friend void swap(Derived& a, Derived& b) noexcept
    {
        using std::swap;
        swap(static_cast<Base&>(a), static_cast<Base&>(b));
        swap(a.m_j, b.m_j);
    }
};
Run Code Online (Sandbox Code Playgroud)

我让编译器隐式地处理析构函数,因为编译器将隐式地给我一个在这种情况下做正确事情的虚拟代码.

我再次明确默认了复制构造函数.这可以纠正您的版本中忽略复制的错误Base.

operator=看起来就像该Base版本.

Derived移动构造函数并不需要移动或复制任何东西other,因为它会swapother.

Derived swap功能必须交换Base零件和Derived零件.


现在考虑使用复制/交换习语.这可以令人惊讶地更容易,并且在某些情况下,性能更高.

对于Base您可以使用= default您的特殊成员的所有5:

class Base
{
    int m_i;
public:
    virtual ~Base() = default;
    Base(const Base&) = default;
    Base& operator=(const Base&) = default;
    Base(Base&&) = default;
    Base& operator=(Base&&) = default;

    explicit Base(int i) noexcept
        : m_i{i}
        {}

    friend void swap(Base& a, Base& b) noexcept
    {
        using std::swap;
        swap(a.m_i, b.m_i);
    }
};
Run Code Online (Sandbox Code Playgroud)

这里唯一需要的工作是自定义构造函数和swap函数.

Derived 更容易:

class Derived
    : public Base
{
    int m_j;
public:
    explicit Derived(int j) noexcept
        : Base(42)
        , m_j{j}
        {}

    friend void swap(Derived& a, Derived& b) noexcept
    {
        using std::swap;
        swap(static_cast<Base&>(a), static_cast<Base&>(b));
        swap(a.m_j, b.m_j);
    }
};
Run Code Online (Sandbox Code Playgroud)

所有 5个特殊成员都可以隐式违约!

我们无法在它们中默认它们,Base因为我们需要指定虚拟析构函数,这会禁止生成移动成员,并且不会使用用户声明的析构函数来生成复制成员.但是因为我们不需要声明析构函数Derived,所以我们可以让编译器处理所有内容.

由于复制/交换的一大卖点是减少编码,因此使用它实际上需要更多编码而不是让编译器默认特殊成员可能具有讽刺意味.

当然,如果默认值不正确,那么不要使用它们.我只是说默认值应该是你的第一选择,在复制/交换之前.