<aside> 💡 Recall: aggregation eg. parts in a catalogue, ducks in a pond
Typical Implementation: Pointer fields
class Pond {
Duck *ducks[MaxDucks];
...
}
Case Study: Does a pointer field always mean non-ownership?
No! Let’s take a close look at lists & nodes
<aside> 💡 Inheritance (Specialization)
Suppose you want to track a collection of Books
class Book {
string title, author;
int length; // # of pages in a book
public:
Book(_________);
...
};
// For textbooks - also a topic:
class Text {
string title, author;
int length;
string topic;
public:
Text(__________);
...
};
// For comic books, want the name of the hero:
class Comic {
string title, author,
int length;
string hero;
public:
Comic(__________);
...
};
This is OK - but doesn’t capture the relationship among Book, Text, Comic
And how do we create an array (or List) that contains a mixture of these?
(the body for the class Comic)
</aside>
<aside> 💡 Could
union BookTypes{Book *b; Text *t; Comic *c;};
BookTypes myBooks[20];
Rather, observe: Texts & Comics are kinds of Books - Books with extra features.
To model in C++ - Inheritance
</aside>
// Bases clss (or **Superclass**)
class Book {
string title, author;
int length;
public:
Book(________);
...
};
// Derived classes (or **Subclasses**)
class Text {
string topic;
public:
Text(________);
...
};
class Comic: public Book {
string hero;
public:
Comic(________);
...
};
<aside> 💡 Derived classes inherit fields & methods from the base class
Who can see these members?
How do we initialize Text? Need title, author, length, topic (title, author, length initialize Book part)
class Text:public Book {
...
public:
Text(string title, string author, int length, string topic):
title{title}, author{author}, length{length}, topic{topic}{}
...
};
Wrong!! for 2 reasons
So a ctor for Book must run before the fields of Text can be initialized
<aside> 💡 If Book has no default ctor, a ctor for Book must be invoked explicitly:
class Text:public Book {
...
public:
Text(string title, string author, int length, string topic):
Book{title, author, length}, topic{topic}{}
// |_________ Step 2 _________| |_ Step 3 _||_| <- Step 4
...
};
</aside>
<aside> 💡 Good reasons to keep superclass fields inaccessible to subclasses:
If you want to give subclasses access to certain members:
protected access:
class Book {
protected:
string title, author; // accessble to Book & its subclasses,
int length; // to no one else
public:
Book(________);
...
};
class Text:public Book {
...
public:
...
void addAuthor(string new Author) {author += newAuthor;}
};
Better: keep fields private, provide protected accessors/mutators
class Book {
string title, author;
int length;
protected:
string getTitle() const; // subclasses can
void setAuthor(string newAuthor); // call these
public:
Book(________);
bool isHeavy() const;
};
Relationship among Text, Comic, Book - called “is - a”
</aside>
Now consider the method is Heavy - when is a Book heavy?
class Book {
...
protected:
int length;
public:
bool isHeavy() const {return length > 200;}
};
class Comic:public Book {
...
public:
...
bool isHeavy() const {return length > 30;}
...
};
etc.