不理解 Stroustup 先生的建议,删除复制默认值并为抽象类 Shape 移动操作
Didn't understand Mr. Stroustup's suggestion to delete the copy default and move operations for the abstract class Shape
我试图理解作者在新书(TCPL第4版)中对3.3.4 Suppressing Operations
的建议,但没有效果。
摘录自本书
对层次结构中的类使用默认的复制或移动是通常是一场灾难:只给一个指向基地的指针,我们根本不会知道派生类有哪些成员(§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;
}
如果你只有一个指向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
。一旦您看到它,这一点就很明显了。
他的意思是,由于潜在的对象切片问题,从外部访问它们是不好的。如果您不需要使类可克隆,那么删除就足够了,否则您可以将其保护为仅在clone()
及其后续版本中可访问。
- 从具有默认值的部分指定模板类继承时发生SWIG错误,具有不带默认值的正向声明
- 有没有办法按值将纯抽象类的所有子类传递给 C++ 中的函数?
- 为什么在使用指针时不采用类成员的默认值,而不是直接实例化对象时?
- 在子类函数覆盖中省略具有默认值的参数
- 为什么不能使用"( )"为类的非静态数据成员提供默认值?
- C++用一个新类初始化二维向量;默认值更改
- 模板类专业化具有成员变量和方法的默认值
- 存储具有许多变体的类的默认值的推荐方法是什么?
- 初始化类成员 - 默认值还是成员初始化列表?
- 具有类成员默认值的 C++11 默认构造函数行为
- CPP 设置子类的默认值
- 为什么在指定其所有模板参数具有默认值的模板类时需要<>?
- 意外地'instantiate'使用大括号初始值设定项的抽象类类型?
- 是否可以在运行时更改C++类的默认值?
- 具有一个参数和默认值的类模板
- 在C 中的自定义类的hashmap中的默认值
- 重写指向抽象类的指针的引用值无效
- 是否可以设计一个包含模板参数默认值的类
- 不理解 Stroustup 先生的建议,删除复制默认值并为抽象类 Shape 移动操作
- 构造函数中抽象类的默认值