通过指向clone
类的指针复制Derived
对象有众所周知的Base
习惯用法。
class Base{
int b;
public:
virtual unique_ptr<Base> clone() const = 0;
virtual ~Base() = default;
};
class Derived : public Base {
int d;
public:
virtual unique_ptr<Base> clone() const override {
return std::make_unique<Derived>(*this);
}
}
但是,在这种情况下,我找不到如何定义复制构造函数和赋值的明确说明。这就是我认为它应该在Base
类:
class Base {
protected:
Base(const Base&) = default;
private:
Base& operator=(const Base&) = delete;
}
是否有必要(为了避免潜在的切片)?这是正确的方法吗?是否足够或者我是否应该将此类声明添加到Derived
类?
答案 0 :(得分:0)
由于派生类使用复制构造函数来创建克隆,因此您可能希望将复制构造函数设置为非公共,以避免意外切片但可以对派生类进行访问。
protected
符合此要求。它需要应用于每个类的复制构造函数,因为编译器生成的复制构造函数是public
。将相同的处理应用于赋值运算符也是有意义的。
这也阻止std::make_unique
访问复制构造函数:
class A
{
protected:
A(A const&) = default;
A& operator=(A const&) = default;
public:
A();
virtual std::unique_ptr<A> clone() const = 0;
};
class B : public A
{
protected:
B(B const&) = default;
B& operator=(B const&) = default;
public:
B();
std::unique_ptr<A> clone() const override {
return std::unique_ptr<A>(new B(*this));
}
};
答案 1 :(得分:0)
除非您需要,否则删除复制赋值运算符可能是一个好主意。
在operator=(const Base&)
中删除Base
就足够了,因为如果基类没有复制赋值运算符,则隐式声明的复制赋值运算符被定义为已删除的派生类(请参阅cppreference.com)
如果您真的想要复制赋值,可以将复制赋值运算符设置为虚拟,并通过
小心地在派生类中实现正确的行为Base::operator=
分配基类成员,dynamic_cast
分配派生类的成员,以确保参数的类型正确。如果操作正确,这可以避免对象切片并保留正确的类型。
示例(省略了复制构造函数详细信息):
struct Point {
virtual Point& operator=(const Point& p) =default;
int x;
int y;
};
struct Point3d :public Point{
virtual Point3d& operator=(const Point& p);
int z;
};
Point3d& Point3d::operator=(const Point& p)
{
Point::operator=(p);
auto p3d = dynamic_cast<const Point3d*>(&p);
if(p3d){
z = p3d->z;
} else {
z = 0;
}
return *this;
}