Polymorphic Big Five
You need to be very careful with Polymorphic Big Five, because there are a few problems that can occur. Learned these in CS247.
Consider the following example:

Text t1{"polymorphism", "Ross", 500, "C++"};
Text t2{"programming for babies", "LaurierProf", 100, "Python"};
Book& br1 = t1;
Book& br2 = t2;
br2 = br1;
cout << t2;- Title, author and length are set, but topic remains unchanged.
Problem 1: Partial Assignment (because of non-virtual)
Book::operator= is defined as non-virtual. We’re calling operator= method on a reference. We use the static type, call Book::operator=, even though br1 and br2 are referencing texts.
Some fields are copied, but not all. This is the partial assignment problem.
How to fix this? Declare Book::operator= as virtual!
class Book {
...
public:
...
virtual Book& operator=(const Book& other) {
...
}
};In Text, we can’t just have the following:
Text& operator=(const Text& other);- Not a valid Function Override (arguments are different)
Signature must be the following
Text& Text::operator=(const Book& other) {}However, trying to fix this partial assignment this way introduces 2 new problems:
- We can’t access
other’s topic, because it’s aBook, andBookdon’t have topics, onlyTextsdo. - other is a
Book&, so now, we get the mixed assignment problem…
Problem 2: Mixed Assignment Problem (because of virtual)
Now, the following is legal:
Comic c{...};
Text t{...};
t = c;We can see that we can set a Text to a Comic, as the Comic on right-hand side can be implicitly converted to a const Book&.
Setting operator= to virtual creates the is the mixed assignment problem, as we can now can set subclass siblings to each other.
In summary
- Non-virtual operator = leads to partial assignment
- virtual operator = mixed assignment
To fix this, we need to restructure the Book hierarchy:

class AbstractBook {
string title, author;
int length;
protected:
AbstractBook& operator=(const AbstractBook& other) = default;
public:
AbstractBook(...) {}
virtual ~AbstractBook() = 0;
};- notice that
operator=is under protected now
To make this abstract, we need a Pure Virtual Method. If no other methods make sense to be pure virtual, can always use destructor.
class Text: public AbstractBook {
string topic;
public:
Text(...) {...}
// implicitly: this is implemented: Text& operator=(const Text& t);
};How does this fix the two problems?
- Mixed assignment:
operator=is non-virtual and the implicitly provided copy assignment operator only acceptsText. - Partial assignment problem:
Text t1{...};
Text t2{...};
AbstractBook& br1 = t1;
AbstractBook& br2 = t2;
br2 = br1; //doesn't compile, AbstractBook:operator= is protectedOnly works since
AbstractBookis an abstract classI am still confused on why the teacher says “only works because it’s abstract class”.
I guess only works is maybe the wrong word. Because we are setting
AbstractBook::operator=to protected. we are ensuring that we can’t assign books to one another. IfBookwas a concrete class, this would be kind of bad, because we DO want to be able to assign books to one another.
TODO BRAINF-CK IDK WHAT’S GOING ON?? Okay, I am getting brainf-cked, wha
- Is it because of how I saw assignment operator doesn’t get inherited?
class AbstractBook {
string title, author;
int length;
protected:
AbstractBook& operator=(const AbstractBook& other) = default;
public:
AbstractBook(...) {}
virtual ~AbstractBook() {};
};
class Text: public AbstractBook {
string topic;
public:
Text() {}
void test() {
Text t1{};
Text t2{};
AbstractBook& br2 = t2;
t1 = br2; // does NOT compile here,why?
}
};- From my understanding,
Textis callingoperator=(const Abstract& other), which it has access to because of inheritance. But even making the function public does not fix the problem. “no operator ”=” matches these operands”
Also see Method Safety Levels, weird stuff going on for what it means to be able to “access” something.
Other small problem: Destructor
Consider Text’s destructor. Implicitly, following happens:
- Destructor body runs (empty)
- Object fields we destructed in reverse decl. order
- Superclass destructor runs
- Space is reclaimed
Because in step 3, Text’s destructor calls AbstractBook’s destructor, we have a problem: we’ve called a method with no implementation.
Solution: give it an implementation:
AbstractBook::~AbstractBook(){}