不明白Stroustup先生的建议是删除抽象类Shape的复制默认和移动操作

时间:2013-07-28 21:04:36

标签: c++ c++11

我试图理解作者在3.3.4 Suppressing Operations新书(TCPL第4版)中的建议,但无济于事。

摘自书籍

  

使用层次结构中的类的默认副本或移动是   通常是一场灾难:只给出指向基地的指针,我们根本就没有   知道派生类有哪些成员(§3.3.3),所以我们不知道如何   复制它们。所以,最好的办法是删除   默认复制和移动操作;也就是说,消除默认值   这两个行动的定义:

class Shape {
public:
    Shape(const Shape&) =delete; // no copy operations
    Shape& operator=(const Shape&) =delete;
    Shape(Shape&&) =delete; //no move operations
    Shape& operator=(Shape&&) =delete;
    ~Shape();
};
  

现在尝试复制Shape将被编译器捕获。如果你   需要复制类层次结构中的对象,编写某种克隆   功能(§22.2.4)。

例如,下面的代码不会使用Shape(const Shape&) = delete;进行编译,因为clone()函数会调用Shape的复制构造函数。

#include <iostream>

class Shape
{
    public:
    virtual ~Shape() {}
    Shape() {}
    Shape(const Shape&) {};
    virtual Shape* clone() const = 0;
};

class Circle: public Shape
{
    public:
    Circle(int i) : a(i) {}
    Circle* clone() const { return new Circle(*this); }
    int a;
};

int main()
{
    Shape* p = new Circle(1);
    Shape* q = p->clone();

    std::cout << dynamic_cast<Circle*>(p)->a << std::endl;
    std::cout << dynamic_cast<Circle*>(q)->a << std::endl; 

}    

2 个答案:

答案 0 :(得分:5)

如果你只有指向Shape的指针,那么你就不可能复制实际的实现 - 它(很可能)会更大,所以你的副本将被“切片”。在您的示例中,Circle将有额外的int a;这不是Shape类的一部分 - 如果你只是简单地复制一个Shape类对象而不知道它是Circle,那将会丢失(并且多态性的全部意义在于你在通用函数中处理它时,不应该“知道”哪个对象是什么类型。

避免因意外使用以下内容而导致的问题:

*q = *p; 

最好“删除”允许你这样做的操作符

但是,由于您描述的情况需要复制构造函数,因此一种解决方案是使其protected - 使用它来防止除派生类之外的其他内容,并且正常工作。

感谢下面的robson(以及夜晚的睡眠),解决方案显然是在Circle中创建一个复制构造函数。仅仅因为你没有Shape并不意味着你不能在派生类中拥有一个:

class Circle: public Shape
{
    public:
    Circle(int i) : a(i) {}
    Circle(const Circle& other) { a = other.a; }    // Note this line!
    Circle* clone() const { return new Circle(*this); }
    int a;
};

它尝试使用Shape复制构造函数的原因是因为你自己的类中没有。你应该!

你也可以(正如罗布森解释的那样):

class Circle: public Shape
{
    public:
    Circle(int i) : a(i) {}
    Circle* clone() const { return new Circle(a); }
    int a;
};

根本不需要复制构造函数。这两个解决方案都解决了“您正在尝试使用已删除的Shape(const Shape &) constructor。一旦您看到它就会很明显。

答案 1 :(得分:1)

他的意思是,由于潜在的物体切片问题,使它们无法从外部进入是不利的。你不需要让类克隆而不是删除就足够了,否则你可以保护它只能在clone()和后继者中访问。