C++ 复印及;基类和派生类中的交换
我最近读到并正在尝试在基类和派生类中实现这些构造函数。我的基类和派生类中都有四个构造函数,但是我不确定如何实现派生类的赋值运算符C++ 复印及;基类和派生类中的交换,c++,c++11,constructor,derived-class,base-class,C++,C++11,Constructor,Derived Class,Base 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(*thi
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);
}
您实现
op=
的方式与派生的
实现Base的方式完全相同:
Derived& operator=(Derived other) { swap(*this, other); return *this; }
不过,我希望您了解按值传递参数的利弊:
- 正面:所有值类别只需要一个函数
- 下侧:第二次移动xvalues,移动prvalues所需副本之外的内容
其他需要考虑的问题:
- 经验法则:单参数非复制/移动构造函数应该是显式的:您真的不想从
int
到Base
进行隐式转换
- 您忘记为
派生的重新实现交换(交换所有子对象,包括基本对象和成员对象)。如果Derived
没有添加任何成员,您可能会放弃它
考虑尽可能多地使用
=default
。如果我们讨论的是公共继承,那么实际上也需要一个虚拟析构函数
以下是使用复制/交换样式时,您的基础
的外观:
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
现在,对于导出的,
:
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);
}
};
这里真正需要做的唯一工作是自定义构造函数和swap
函数
派生的
更容易:
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);
}
};
派生类
:公共基地
{
国际博物馆;
公众:
显式派生(int j)noexcept
:底座(42)
,m_j{j}
{}
朋友无效掉期(衍生a、衍生b)无例外
{
使用std::swap;
交换(静态广播(a)、静态广播(b));
掉期(a.m_j,b.m_j);
}
};
所有5个特殊成员都可以隐式默认
我们不能在Base
中默认它们,因为我们需要指定虚拟析构函数,它禁止生成移动成员,并且不推荐使用用户声明的析构函数生成复制成员。但是,由于我们不需要在派生的
中声明析构函数,我们可以让编译器处理所有事情
由于复制/交换的一大卖点是减少编码,因此具有讽刺意味的是,使用它实际上需要比让编译器默认特殊成员更多的编码
当然,如果默认值做不到正确的事情,那么就不要使用它们。我只是说,在复制/交换之前,默认值应该是您的第一选择。重击规则:单参数非复制/移动构造函数应该是显式的:您真的不想从int
隐式转换到Base
。此外,您还需要在派生的中定义swap
,除非Base
的是好的enough@Deduplicator:谢谢你的提示,我编辑了我的问题。@Jon:但我喜欢用拇指捶打;-)(谢谢)哈哈!C++确实有一些拇指规则,人们应该因为不跟随而被大打出手!所以你根本不调用任何基的向量,在派生中做所有必要的事情?@gartenriese:是的,至少不直接。(所有派生的构造函数都调用一个基本的构造函数,而派生的::swap需要调用Base::swap)啊,你的派生的move构造函数是完全错误的:它需要廉价地初始化对象,然后交换所有东西。(如果默认的ctor或swap不便宜,则需要对其进行自定义编码)。这就引出了一个问题:为什么你没有一个默认的ctor?(可能会为ctor(int)添加一个默认值)“我再次明确默认了复制构造函数。这纠正了您的版本中忽略复制基类的错误。”-这是否意味着默认复制ctor调用其基类的复制ctor?@gartenriese:这是正确的。所有默认的特殊成员将首先在基上迭代,然后在非静态数据成员上迭代,依次对每个基执行指定的操作。哦,除了析构函数以构造函数的相反顺序执行这个迭代。好的,谢谢!顺便说一句,我不能使用默认值,因为我必须复制一些OpenGL的东西。“派生的移动构造函数不需要移动或复制其他任何东西,因为它将与其他对象交换”,-不交换实际移动的东西吗?或者你的意思是“通过直接调用”?a如果使用复制和移动构造函数的默认版本,为什么需要实现交换?
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);
}
};