A common, simple, use of multiple inheritance to combine two essentially unrelated classes:
class ReadWrite : public Reader, public Writer { ... };
Remember that we can also combine classes using subobjects:
class ChessGame : public Window {
protected:
Board board;
...
};
This prevents clients from circumventing the rules of chess.
Key question: should the new class be usable by clients of the old?
Often a class extends a concrete base class and an abstract one, using the concrete class to implement the undefined methods from the second class, and possibly a bit more:
class ActiveGrid : public Grid, public ButtonListener {
public:
void mouse_pressed(ButtonEvent & e) {
// use Grid stuff
}
};
Java supports only this special case.
What if two base classes define the same name?
class A { public: int f(); };
class B { public: int f(); };
class AB : public A, public B {
public:
int g() {
return f() + 1; // which one?
}
};
In C++, ambiguous names must be qualified:
class A { public: int f(); };
class B { public: int f(); };
class AB : public A, public B {
public:
int g() {
return A::f() + B::f() + 1;
}
};
There is no way to redefine virtual methods whose names clash
in A and B.
class Storable { ... };
class Transmitter : public Storable { ... };
class Receiver : public Storable { ... };
class Radio : public Transmitter, public Receiver { ... };
class Storable {
public:
virtual void write() = 0;
};
class Transmitter : public Storable {
public:
virtual void write() { ... }
};
class Receiver : public Storable {
public:
virtual void write() { ... }
};
A virtual function in the replicated base class can be overridden:
class Radio : public Transmitter, public Receiver {
public:
virtual void write() {
Transmitter::write();
Receive::write();
// write extra Radio stuff
}
};
The use of the base class versions, plus a bit more, is common.
Suppose we want:
If we write
class Window { ... };
class WindowWithBorder : public virtual Window { ... };
class WindowWithMenu : public virtual Window { ... };
class Painter : public WindowWithBorder,
public WindowWithMenu { ... };
then a Painter object includes a single Window.
It is a virtual base class.
class Window {
public:
Window(int i) { ... }
};
class WindowWithBorder : public virtual Window {
public:
WindowWithBorder() : Window(1) { ... }
};
class WindowWithMenu : public virtual Window {
public:
WindowWithMenu() : Window(2) { ... }
};
The parameters of the constructor of a virtual base class are specified (implicitly or explicitly) by constructors of the closest derived class:
class Painter : public WindowWithBorder,
public WindowWithMenu {
public:
Painter(int i) : Window(i), WindowWithBorder(),
WindowWithMenu() { ... }
...
};
This avoids any conflict between the constructors of the intermediate classes.
The language ensures that each constructor is called exactly once.
Suppose the virtual base class has a method that is redefined by each base class
class Window {
public:
virtual void draw() {
// draw window
}
};
class WindowWithBorder : public virtual Window {
public:
virtual void draw() {
Window::draw();
// draw border
}
};
class WindowWithMenu : public virtual Window {
public:
virtual void draw() {
Window::draw();
// draw menu
}
};
But then if we write
class Painter : public WindowWithBorder,
public WindowWithMenu {
void draw() {
WindowWithBorder::draw();
WindowWithMenu::draw();
// draw Painter stuff
}
};
the Window is drawn twice.
We put the drawing of the extra stuff in a method of its own:
class WindowWithBorder : public virtual Window {
public:
void own_draw() { ... }
virtual void draw() {
Window::draw();
own_draw();
}
};
and similarly for WindowWithMenu.
class Painter : public WindowWithBorder,
public WindowWithMenu {
void own_draw();
void draw() {
Window::draw();
WindowWithBorder::own_draw();
WindowWithMenu::own_draw();
own_draw();
}
};
Then each part is drawn exactly once.
class ios {
// private state
public:
bool good() const { ... }
bool eof() const { ... }
bool fail() const { ... }
bool bad() const { ... }
};
class istream : virtual public ios { ... };
class ostream : virtual public ios { ... };
class iostream : public istream, public ostream {};
The state of ios is not duplicated.