9

我试图理解作者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; 

}    
4

2 回答 2

5

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

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

*q = *p; 

最好“删除”允许您这样做的运算符

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

多亏了下面的罗布森(以及晚上睡觉),解决方案显然是在Circle. 仅仅因为你没有一个 forShape并不意味着你不能在派生类中有一个:

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复制构造函数的原因是您自己的类中没有复制构造函数。你应该!

你也可以这样做(正如 Robson 解释的那样):

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

并且根本不需要复制构造函数。这两种解决方案都解决了“您正在尝试使用已删除的Shape(const Shape &) constructor.

于 2013-07-28T21:21:27.500 回答
1

他的意思是,由于潜在的对象切片问题,从外部访问它们是不好的。如果您不需要使类可克隆而不是删除就足够了,否则您只需将其保护为只能在其内部clone()和后继者中访问。

于 2013-07-28T21:19:01.610 回答