Rob*_*Rob 7 c++ virtual class copy-and-swap
我正在尝试使用纯虚方法和'复制和交换'惯用法实现虚拟类,但我遇到了一些问题.代码将无法编译,因为我在包含纯虚方法的类A的assign运算符中创建实例.
有没有办法如何使用纯虚方法和复制和交换成语?
class A
{
public:
A( string name) :
m_name(name) { m_type = ""; }
A( const A & rec) :
m_name(rec.m_name), m_type(rec.m_type) {}
friend void swap(A & lhs, A & rhs)
{
std::swap(lhs.m_name, rhs.m_name);
std::swap(lhs.m_type, rhs.m_type);
}
A & operator=( const A & rhs)
{
A tmp(rhs);
swap(*this, tmp);
return *this;
}
friend ostream & operator<<( ostream & os,A & x)
{
x.print(os);
return os;
}
protected:
virtual void print(ostream & os) =0;
string m_type;
string m_name;
};
class B : A
{
public:
B(string name, int att) :
A(name),
m_att(att)
{
m_type="B";
}
B( const B & rec) :
A(rec),
m_att(rec.m_att) {}
friend void swap(B & lhs, B & rhs)
{
std::swap(lhs.m_att, rhs.m_att);
}
B & operator=( const B & rec)
{
B tmp(rec) ;
swap(*this, tmp);
return *this;
}
private:
virtual void print(ostream & os);
int m_att;
};
Run Code Online (Sandbox Code Playgroud)
错误信息:
In member function ‘A& A::operator=(const A&)’:|
error: cannot declare variable ‘tmp’ to be of abstract type ‘A’|
because the following virtual functions are pure within ‘A’:|
virtual void A::print(std::ostream&)|
Run Code Online (Sandbox Code Playgroud)
正如编译器通知您的那样,您无法创建抽象类型的变量。没有办法围绕这个跳舞。
这给你留下了三个主要选择:
首先,您可以摆脱纯虚方法,并在每个调用的方法中提供一个小存根std::terminate,这显然会破坏编译时检测所有(以前的)纯虚方法是否在所有派生类中被重写。
这将导致切片,因为它只会复制基类,而构成派生类的所有内容都会丢失。
与此类似,您可以创建一个派生类,该派生类使用简单的存根(可能调用std::terminate)来实现所有虚拟方法,并且仅用作“基类的可实例化版本”。
该类要实现的最重要的部分是一个构造函数,它接受基类的 const 引用,因此您可以只使用它而不是复制基类。这个例子还添加了一个移动构造函数,因为我是一个性能崇拜者。
这会导致与第一个选项相同的切片问题。根据您正在做的事情,这可能是您的预期结果。
struct InstantiatableA : public A {
InstantiatableA(A const& rhs) : A(rhs) { }
InstantiatableA(A&& rhs) : A(::std::move(rhs)) { }
void print(ostream&) override { ::std::terminate(); }
};
A& A::operator=(InstantiatableA rhs) {
using ::std::swap;
swap(*this, rhs);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
注意:这确实是一个类型的变量A,虽然我说这是做不到的。您唯一需要注意的是类型变量A存在于类型变量内部InstantiatableA!
最后,您可以将 a 添加virtual A* copy() = 0;到基类中。然后,您的派生类B必须将其实现为A* copy() override { return new B(*this); }. 动态内存是必要的原因是因为您的派生类型可能比您的基类需要任意更多的内存。