Session 8: Multiple Inheritance

Major Differences between Java and C++

Multiple Inheritance

The simple case

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?

An asymmetrical case

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.

Name clashes (ambiguity)

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

Possible solutions

Renaming the methods in the original classes is often not an option, as they may be part of a library or fixed interface.

Implicit ambiguity resolution is dangerous

Ambiguity resolution by qualification

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.

Renaming

Replicated base classes

    class Storable { ... };

    class Transmitter : public Storable { ... };

    class Receiver : public Storable { ... };

    class Radio : public Transmitter, public Receiver { ... };

Replicated base classes, graphically

205

Virtual functions in the base class

    class Storable {
    public:
            virtual void write() = 0;
    };

    class Transmitter : public Storable {
    public:
            virtual void write() { ... }
    };

    class Receiver : public Storable {
    public:
            virtual void write() { ... }
    };

Overriding virtual methods

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.

Repeated inheritance (sharing)

Suppose we want:

206

Virtual base class

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.

Constructors

    class Window {
    public:
            Window(int i) { ... }
    };

    class WindowWithBorder : public virtual Window {
    public:
            WindowWithBorder() : Window(1) { ... }
    };

    class WindowWithMenu : public virtual Window {
    public:
            WindowWithMenu() : Window(2) { ... }
    };

Constructors for a virtual base class

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.

Ensuring other methods are called only once

Suppose the virtual base class has a method that is redefined by each base class

    class Window {
    public:
            virtual void draw() {  
                    // draw window
            }
    };

Drawing, first attempt


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

Disaster

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.

Solution: auxiliary methods

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.

Calling each method once

    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.

Repeated inheritance: Summary

I/O stream classes

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

Stream class hierarchy

207

The state of ios is not duplicated.

Next week: memory management