Skip to content

Latest commit

 

History

History
112 lines (86 loc) · 2.78 KB

problem_21.md

File metadata and controls

112 lines (86 loc) · 2.78 KB

I want a class with no objects << | Home | >> I want to know what kind of Book I have

Problem 21 - The copier is broken

How do copies and moves interact with inheritance?

Copy constructor:

Text::Text(const Text &other): Book{other}, topic{other.topic} {}

Move constructor:

Text::Text(const Text &other): 
    Book{std::move(other)}, 
    topic{std::move(other.topic)} {}

We can still call std::move(other.topic) even though we already moved other because std::move(other) only took the "Book part", leaving other.topic behind.

Copy/Move assignment:

Text &Text::operator=(Text other) {
    Book::operator=(std::move(other));
    topic = std::move(other.topic); 
}

But consider:

Book *b1 = new Text{...};  // Author1 writes about BASIC
Book *b2 = new Text{...};  // Author2 writes C++

*b1 = *b2;

Now Author2 writes about BASIC and Author1 writes about C++

  • Essentially only the book part gets copied over
  • Partial Assignment
  • Topic doesn't match title and author - as a Book this is valid, as a Text it is corrupted

Possible solution: make operator= virtual

class Book {
        ...
    public:
        virtual Book &operator=(const Book &other);
        ...
};

class Text: public Book {
        ...
    public:
        Text &operator=(const Text &other) override;
        ...
};

Doesn't compile, Text &operator= must take a Book or it's no an override

class Text: public Book {
        ...
    public:
        Text &operator=(const Book &other) override;
        ...
};

But then we could pass a Comic which is also a problem. We will revisit this later.

Also note that we can return a Text & as opposed to a Book & in class Text. This is because we're returning a reference of a subclass, which is allowed.

Another solution: make all superclasses abstract

Instead of:

  • Book

    • Text
    • Comic
  • AbstractBook

    • NormalBook
    • Text
    • Comic
class AbstractBook {
        ...
    protected:
        AbstractBook &operator=(AbstractBook other) { ... } // Non-virtual
};

class Text: public AbstractBook {
     public:
        Text &operator=(Text other) {
            AbstractBook::operator=(std::move(other));
            topic = std::move(other.topic);
        }
};

Since operator= is non-virtual, there is no mixed assignment!

AbstractBook::operator= not accessible to outsiders.

Now *b1 = *b2 won't compile.

Basically saying, before you assign something, understand what you're assigning and do it directly rather than through your superclass.


I want a class with no objects << | Home | >> I want to know what kind of Book I have