Didn’t understand Mr. Struustup’s suggestion to remove the default operations and move for the Shape abstract class

I am trying to understand what the author offers in 3.3.4 Suppressing Operations in his new book (4th edition of TCPL), but to no avail.

Excerpt from the book

Using the default copy or move for a class in a hierarchy is typically a disaster: given only a pointer to the base, we simply don’t know which members of the derived class have (§3.3.3), so we cannot know how to copy them. So, it’s best to do this, usually to remove the copy and move operations by default; that is, exclude the default definition of these two operations:

 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(); }; 

Now an attempt to copy the figure will catch the compiler. if you need to copy an object into a class hierarchy, write some kind of function clone (§22.2.4).

For example, the code below does not compile with Shape(const Shape&) = delete; since the clone() function calls the Shape copy constructor.

 #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; } 
+8
c ++ c ++ 11
source share
2 answers

If you only have a pointer to Shape , then you cannot make a copy of the actual implementation - it (most likely) will be larger, so your copy will be "sliced". In your example, Circle will have an extra int a ; which is not part of the Shape class, which will be lost if you simply copy an object of the Shape class without knowing its Circle (and the whole point of polymorphism is that you do not have to ā€œknowā€ which object is a type when working with it in general features "

To avoid problems caused by accidental use of something like:

 *q = *p; 

it’s better to ā€œdeleteā€ statements that allow you to do this

However, since the copy constructor is necessary for the case you are describing, therefore, one solution is to make it protected - protecting it from something other than a derived class that uses it and works correctly.

Thanks to the burglar below (as well as night sleep), the solution clearly should make a constructor copy in Circle . Just because you don’t have one for Shape does not mean that you cannot have it in a derived class:

 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; }; 

The reason he is trying to use the Shape constructor is because you don't have one in your class. You should!

You can also do (as Robson explained):

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

And generally do not need a copy constructor. Both of these solutions allow "you are trying to use a remote Shape(const Shape &) constructor . This is really obvious as soon as you see it.

+5
source share

He meant that it was bad to make them accessible from the outside, due to problems with overlaying objects. You do not need to do the cloning of the class, except for deletion, enough, otherwise you can just make it protected accessible only within clone() and successors.

+1
source share

All Articles