我最近阅读了copy & swap,现在正尝试在基类和派生类中实现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);
}
答案 0 :(得分: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);
}
};
与您所拥有的唯一区别在于我添加了虚拟析构函数,并使用= 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);
}
};
我让编译器隐式地处理析构函数,因为编译器会隐式地给我一个在这种情况下做正确事情的虚拟代码。
我再次明确默认了复制构造函数。这可以纠正您的版本中的一个错误,该错误忽略了复制Base
。
operator=
看起来就像Base
版本。
Derived
移动构造函数不需要移动或复制other
中的任何内容,因为它swap
会other
。
Derived
swap
函数必须交换Base
部分以及Derived
部分。
现在考虑不使用复制/交换习惯用法。这可以令人惊讶地更容易,并且在某些情况下,表现更高。
对于Base
,您可以对所有5位特殊成员使用= default
:
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
函数。
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);
}
};
所有 5个特殊成员都可以隐式默认!
我们无法在Base
中默认它们,因为我们需要指定虚拟析构函数,这会禁止生成移动成员,并且不会使用用户声明的析构函数来生成复制成员。但是因为我们不需要在Derived
中声明析构函数,所以我们可以让编译器处理所有内容。
由于复制/交换的一大卖点是减少编码,因此使用它实际上需要更多编码而不是让编译器默认特殊成员,这可能具有讽刺意味。
当然,如果默认设置不正确,那么不要使用它们。我只是说默认值应该是你的第一选择,在复制/交换之前。
答案 1 :(得分:3)
op=
与Derived
完全相同[{1}}实施Base
:
Derived& operator=(Derived other) { swap(*this, other); return *this; }
我希望你知道在那里通过值传递参数的上下方面,但是:
需要考虑的其他要点:
explicit
:您真的不希望从int
到{{1}进行隐式转换} ... Base
重新实现swap
(交换所有子对象,包括基础和成员)。如果Derived
没有添加任何成员,您可能会放弃它。