C++ 使用纯虚拟类复制和交换习惯用法
我试图用纯虚拟方法和“复制和交换”习惯用法实现虚拟类,但遇到了一些问题。代码不会编译,因为我正在类A的赋值操作符中创建实例,该类包含纯虚方法 有没有办法使用纯虚拟方法和复制交换习惯用法C++ 使用纯虚拟类复制和交换习惯用法,c++,class,virtual,copy-and-swap,C++,Class,Virtual,Copy And Swap,我试图用纯虚拟方法和“复制和交换”习惯用法实现虚拟类,但遇到了一些问题。代码不会编译,因为我正在类A的赋值操作符中创建实例,该类包含纯虚方法 有没有办法使用纯虚拟方法和复制交换习惯用法 class A { public: A( string name) : m_name(name) { m_type = ""; } A( const A & rec) : m_name(rec.m_name), m_type(rec.m_ty
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;
};
编译器告诉您,不能创建抽象类型的变量。没有办法绕着它跳舞 这将为您留下三个主要选项: 停止使用纯虚拟函数 首先,您可以去掉纯虚拟方法,并在每个调用的方法中提供一个小存根,这显然会破坏编译时对是否在所有派生类中重写所有(以前的)纯虚拟方法的检测 这将导致,因为它只会复制基类,而构成派生类的所有内容都将丢失 使用不带纯虚拟函数的存根类 与此类似,您可以创建一个派生类,该类使用简单的存根(可能是调用)实现所有虚拟方法,并且仅用作“基类的可实例化版本” 这个类要实现的最重要的部分是一个构造函数,它接受基类的const引用,因此您可以使用它而不是复制基类。这个例子还添加了一个move构造函数,因为我是一个性能迷 这会导致与第一个选项相同的问题。根据你所做的,这可能是你预期的结果
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;
}
注意:这实际上是一个a
类型的变量,尽管我说过它无法实现。唯一需要注意的是A
类型的变量存在于instantiablea
类型的变量中
使用复制工厂
最后,您可以添加一个
虚拟a*copy()=0代码>到基类。然后,派生类B
必须将其实现为A*copy()重写{returnnewb(*this);}
。之所以需要动态内存,是因为派生类型可能需要比基类任意多的内存。编译器是正确的。类A
是抽象类,因此不能在操作符=
中创建它的实例
在B中,您刚刚声明了print
函数,但没有实现它。也就是说,您将得到链接错误
通过实现它,它可以很好地编译(如果我们忽略各种警告):
void B::print(ostream&os)
{
os您只需要面对这样一个事实,即继承在复制语义方面的工作非常笨拙
例如,假设您发现了一个通过编译阶段的技巧,这意味着什么(以下示例使用赋值,但问题与副本相同):
CRTP是一种选择:
template<typename swappable>
struct copy_swap_crtp{
auto& operator=(copy_swap_crtp const& rhs){
if (this==std::addressof(tmp))
return *this;
swappable tmp{rhs.self()};
self().swap(tmp);
return *this;
};
auto& operator=(copy_swap_crtp && rhs){
self().swap(rhs.self());
return *this;
};
protected:
auto& self(){return *static_cast<swappable*>(this);};
//auto& self()const{return *static_cast<swappable const*>(this);};
};
模板
结构复制\u交换\u crtp{
自动和操作员=(复制\u交换\u crtp const和rhs){
if(this==std::addressof(tmp))
归还*这个;
可交换tmp{rhs.self()};
自交换(tmp);
归还*这个;
};
自动和操作员=(复制\u交换\u crtp和&rhs){
self().swap(rhs.self());
归还*这个;
};
受保护的:
auto&self(){return*static_cast(this);};
//auto&self()常量{return*static_cast(this);};
};
用户类别:
struct copy_swap_class
: copy_swap_crtp<copy_swap_class>
{
copy_swap_class(copy_swap_class const&);
void swap(copy_swap_class&);
};
struct copy\u swap\u类
:复制\u交换\u crtp
{
复制交换类(复制交换类常量&);
无效交换(复制交换类&);
};
干杯,
FM.你不能有tmp(rhs);
A
是一个抽象类。@MohitJain我想他很清楚这一点。这就是为什么他问如何解决这个问题。复制和交换习惯用法不应该与虚拟函数/类层次结构相结合。这会导致切片(和UB)和缓慢的实现(因为您的临时复制对象创建了一个完整的虚拟函数表)。相反,如果您希望使用虚拟基类进行复制/赋值,请将您的实现基于一个。顺便说一句:复制和交换的最大优势在于执行&operator=(a rhs){swap(*this,rhs);return*this;}这并不能真正回答OP的问题。“致命吉他:我想我是这么做的。这是因为,在我看来,这个问题在没有产生一个大的设计问题的情况下是不能被正确回答的。所以它回答了这个问题,说最初的问题是希望实现一个实体类型的拷贝和交换。我想,你可以考虑编辑。把这个写进你的答案里,让它更清楚。
// class A
// a class B : public A
// another class C : public A inheriting publicly from A
// another class D : public B inheriting publicly from B
B b1;
C c1;
D d1;
// Which semantic for following valid construction when copy/assignment is defined in A ?
b1 = c1;
b1 = d1;
A &ra = b1;
B b2;
// Which semantic for following valid construction when copy/assignment is defined in A ?
ra = b2;
ra = c1;
ra = d1;
template<typename swappable>
struct copy_swap_crtp{
auto& operator=(copy_swap_crtp const& rhs){
if (this==std::addressof(tmp))
return *this;
swappable tmp{rhs.self()};
self().swap(tmp);
return *this;
};
auto& operator=(copy_swap_crtp && rhs){
self().swap(rhs.self());
return *this;
};
protected:
auto& self(){return *static_cast<swappable*>(this);};
//auto& self()const{return *static_cast<swappable const*>(this);};
};
struct copy_swap_class
: copy_swap_crtp<copy_swap_class>
{
copy_swap_class(copy_swap_class const&);
void swap(copy_swap_class&);
};