考虑尽可能使用
= 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
中移动或复制任何内容,因为它将要与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
中声明析构函数,所以我们可以让编译器处理所有内容。
作为复制/交换的一个重要卖点是减少编码量,讽刺的是使用它实际上可能需要比让编译器默认设置特殊成员更多的编码。
当然,如果默认值不能做正确的事情,那么就不要使用它们。我只是说,默认值应该是你的首选,而不是复制/交换。
int
到Base
进行隐式转换... - DeduplicatorDerived
中定义swap
,除非Base
中的那个足够好。 - Deduplicator