复制和交换基类和派生类 [英] copy & swap in base and derived class

查看:66
本文介绍了复制和交换基类和派生类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近阅读了有关 copy&swap ,现在正尝试在基类和派生类中实现ctor.我的基类和派生类中都有四个构造函数,但是我不确定如何实现派生类的赋值运算符.

I recently read about copy & swap and am now trying to implement the ctors in a base and derived class. I have the four constructors in both my base and derived class, however I am unsure how to implement the assignment operator of the derived class.

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);
}

推荐答案

考虑尽可能使用 = default .而且,如果我们谈论的是公共继承,那么您确实也需要一个虚拟析构函数.

Consider using = default as much as possible. And if we are talking about public inheritance, you really need a virtual destructor as well.

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

Here is how your Base would look using the copy/swap style:

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);
    }
};

与您所拥有的唯一不同之处在于,我添加了虚拟析构函数,并将 = default 用作副本构造函数.

The only difference from what you have is that I've added the virtual destructor, and used = default for the copy constructor.

现在表示派生:

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);
    }
};

我已经让编译器隐式地处理析构函数,因为在这种情况下,编译器将隐式地给我一个虚拟的,可以正确地执行操作.

I've let the compiler implicitly take care of the destructor since the compiler will implicitly give me a virtual one that does the right thing in this case.

同样,我已经明确地默认了拷贝构造函数.这会纠正您的版本中的一个错误,该错误忽略了复制 Base .

Again I've explicitly defaulted the copy constructor. This corrects a bug in your version which neglects to copy Base.

operator = 看起来像 Base 版本.

派生移动构造函数不需要从 other 移动或复制任何内容,因为它将与 other .

The Derived move constructor does not need to move or copy anything from other since it is going to swap with other.

Derived swap 函数必须交换 Base 部分和 Derived 部分.

The Derived swap function must swap the Base part as well as the Derived part.

现在考虑使用复制/交换习语.这可能出奇的容易,在某些情况下还可以提高性能.

Now consider not using the copy/swap idiom. This can be surprisingly easier, and in some cases, higher performing.

对于基本,您可以对所有5个特殊成员使用 = default :

For Base you can use = default for all 5 of your special members:

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);
    }
};

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

The only work that is really required here is your custom constructor and swap function.

派生更容易:

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);
    }
};

所有 5个特殊成员可以被隐式默认!

All 5 of the special members can be implicitly defaulted!

我们无法在 Base 中将它们默认为默认值,因为我们需要指定虚拟析构函数,该虚构析构函数禁止移动成员的生成,而复制成员的生成已被用户弃用-声明的析构函数.但是,由于我们不需要在 Derived 中声明析构函数,因此我们可以让编译器处理所有事情.

We couldn't default them in the Base because we needed to specify the virtual destructor, which inhibits the generation of the move members, and the generation of the copy members is deprecated with a user-declared destructor. But since we do not need to declare the destructor in Derived, we can just let the compiler handle everything.

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

As one of the big selling points of copy/swap is reduced coding, it can be ironic that using it can actually require more coding than letting the compiler default the special members.

当然,如果默认设置不能正确执行操作,则不要使用它们.我只是说默认值应该是您的第一选择,而不是复制/交换.

Of course if the defaults do not do the right thing, then don't use them. I'm simply saying that the defaults should be your first choice, ahead of copy/swap.

这篇关于复制和交换基类和派生类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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