CS253: Software Development with C++

Fall 2020

Multiple Inheritance

Show Lecture.MultipleInheritance as a slide show.

CS253 Multiple Inheritance


Vishnu and his avatars

Multiple Aspects

Multiple Inheritance

Example

class Citizen {
  public:
    void vote() { cout << "Stupid electoral college!\n"; }
};

class Teacher {
  public:
    void lecture() { cout << "Avoid .eof()!\n"; }
};

class Jack : public Citizen, public Teacher {
  public:
    void relax() { cout << "Read a comic book.\n"; }
};

Jack j;
j.vote();       // method inherited from Citizen
j.lecture();    // method inherited from Teacher
j.relax();      // method of Jack
Stupid electoral college!
Avoid .eof()!
Read a comic book.

Example

class Citizen {
  public:
    void vote() { cout << "Stupid electoral college!\n"; }
};

class Teacher {
  public:
    void lecture() { cout << "Avoid .eof()!\n"; }
};

class Jack : public Citizen, public Teacher {
  public:
    void relax() { cout << "Read a comic book.\n"; }
};

Jack j;
Citizen *c = &j;
c->vote();
Stupid electoral college!

Example

class Citizen {
  public:
    void vote() { cout << "Stupid electoral college!\n"; }
};

class Teacher {
  public:
    void lecture() { cout << "Avoid .eof()!\n"; }
};

class Jack : public Citizen, public Teacher {
  public:
    void relax() { cout << "Read a comic book.\n"; }
};

Jack j;
Citizen *c = &j;
c->vote();      // ok
c->lecture();   // Not a method of Citizen
c->relax();     // Not a method of Citizen
c.cc:19: error: 'class main()::Citizen' has no member named 'lecture'
c.cc:20: error: 'class main()::Citizen' has no member named 'relax'

No, virtual doesn’t help

class Citizen {
  public:
    virtual void vote() { cout << "Stupid electoral college!\n"; }
};

class Teacher {
  public:
    virtual void lecture() { cout << "Avoid .eof()!\n"; }
};

class Jack : public Citizen, public Teacher {
  public:
    virtual void relax() { cout << "Read a comic book.\n"; }
};

Jack j;
Citizen *c = &j;
c->vote();      // ok
c->lecture();   // fails even with virtual
c->relax();     // fails even with virtual
c.cc:19: error: 'class main()::Citizen' has no member named 'lecture'
c.cc:20: error: 'class main()::Citizen' has no member named 'relax'

Ambiguity

class Base1 { public: const int x=11; };
class Base2 { public: const int x=22; };
class Derived : public Base1, public Base2 {
  public:
    const int y = 33;
};

int main() {
    Derived d;
    cout << d.x << '\n';  // Ambiguous.
    cout << d.y << '\n';  // Only one y, in Derived.
}
c.cc:10: error: request for member 'x' is ambiguous
c.cc:2: note: candidates are: 'const int Base2::x'
c.cc:1: note:                 'const int Base1::x'

Resolving Ambiguity

class Base1 { public: const int x=11; };
class Base2 { public: const int x=22; };
class Derived : public Base1, public Base2 {
  public:
    const int y = 33;
};

int main() {
    Derived d;
    cout << d.Base2::x << '\n';  // Dreadful, but works.
    cout << d.y << '\n';  // Only one y, in Derived.
}
22
33

Space Considerations

I’m running out of room, so I’m going to use struct instead of class in the declarations. In a struct, things are public by default, so I can omit the public: lines, and also the public in inheritance.

Multiple Levels

struct Worker {
    Worker() { clog << "Worker\n"; }
    int ss_num;
};
struct CSU_Employee : Worker {
    int employee_id;
};
struct Jack : CSU_Employee {
    int comics;
};

int main() {
    Jack j;
    j.comics = 1000003;
    j.employee_id = 812345678;
    j.ss_num = 362269871;
    cout << j.employee_id;
}
Worker
812345678

Same thing, but squished

struct Worker { Worker() { clog << "Worker\n"; } int ss_num; };
struct CSU_Employee : Worker { int employee_id; };
struct Jack : CSU_Employee { int comics; };

int main() {
    Jack j;
    j.comics = 1000003;
    j.employee_id = 812345678;
    j.ss_num = 362269871;
    cout << j.employee_id;
}
Worker
812345678

The only change is whitespace.

Multiple Inheritance

The Dreaded Diamond

struct Worker { Worker() { clog << "Worker\n"; } int ss_num; };
struct CSU_Employee : Worker { int employee_id; };
struct FCC_Employee : Worker { int employee_id; };
struct Jack : CSU_Employee, FCC_Employee { int comics; };

int main() {
    Jack j;
    j.comics = 1000003;     // OK
    j.employee_id = 3; // ambiguous
    j.employee_id = 812345678; // ambiguous
    j.ss_num = 362269871;   // ambiguous
}
c.cc:9: error: request for member 'employee_id' is ambiguous
c.cc:3: note: candidates are: 'int FCC_Employee::employee_id'
c.cc:2: note:                 'int CSU_Employee::employee_id'
c.cc:10: error: request for member 'employee_id' is ambiguous
c.cc:3: note: candidates are: 'int FCC_Employee::employee_id'
c.cc:2: note:                 'int CSU_Employee::employee_id'
c.cc:11: error: request for member 'ss_num' is ambiguous
c.cc:1: note: candidates are: 'int Worker::ss_num'
c.cc:1: note:                 'int Worker::ss_num'

There are two employee_id variables, and two ss_num variables.

Inheritance hierarchy

    ┌──────────────┐      ┌──────────────┐
    │    Worker    │      │    Worker    │
    └──────────────┘      └──────────────┘
            △                     △
            │                     │
    ┌───────┴──────┐      ┌───────┴──────┐
    │ CSU_Employee │      │ FCC_Employee │
    └──────────────┘      └──────────────┘
            △                     △
            │                     │
            └─────────┬───────────┘
                      │
                   ┌──┴───┐
                   │ Jack │
                   └──────┘

Fix #1

struct Worker { Worker() { clog << "Worker\n"; } int ss_num; };
struct CSU_Employee : Worker { int employee_id; };
struct FCC_Employee : Worker { int employee_id; };
struct Jack : CSU_Employee, FCC_Employee { int comics; };

int main() {
    Jack j;
    j.comics = 1000003;
    j.FCC_Employee::employee_id = 3;
    j.CSU_Employee::employee_id = 812345678;
    j.ss_num = 362269871;   // still ambiguous
}
c.cc:11: error: request for member 'ss_num' is ambiguous
c.cc:1: note: candidates are: 'int Worker::ss_num'
c.cc:1: note:                 'int Worker::ss_num'

Problem #2

    ┌──────────────┐      ┌──────────────┐
    │    Worker    │      │    Worker    │
    └──────────────┘      └──────────────┘
            △                     △
            │                     │
    ┌───────┴──────┐      ┌───────┴──────┐
    │ CSU_Employee │      │ FCC_Employee │
    └──────────────┘      └──────────────┘
            △                     △
            │                     │
            └─────────┬───────────┘
                      │
                   ┌──┴───┐
                   │ Jack │
                   └──────┘

Using virtual inheritance

struct Worker { Worker() { clog << "Worker\n"; } int ss_num; };
struct CSU_Employee : virtual Worker { int employee_id; };
struct FCC_Employee : virtual Worker { int employee_id; };
struct Jack : CSU_Employee, FCC_Employee { int comics; };

int main() {
    Jack j;
    j.comics = 1000003;
    j.FCC_Employee::employee_id = 3;
    j.CSU_Employee::employee_id = 812345678;
    j.ss_num = 362269871;
}
Worker

New Inheritance Hierarchy

        	┌──────────────┐
        	│    Worker    │
        	└──────────────┘
                       △
                       │
                       │
            ┌──────────┴──────────┐
            │                     │
    ┌───────┴──────┐      ┌───────┴──────┐
    │ CSU_Employee │      │ FCC_Employee │
    └──────────────┘      └──────────────┘
            △                     △
            │                     │
            └──────────┬──────────┘
                       │
                    ┌──┴───┐
                    │ Jack │
                    └──────┘

Order of construction

This uses our class Loud, which displays a message for every method invoked.

class Gamma : public Loud {
  public:
    Gamma() : beta('b'), Loud('c'), alpha('a') {
            clog << __func__ << '\n';
    };
    ~Gamma() { clog << __func__ << '\n'; }
  private:
    Loud alpha, beta;
};

Gamma g;
Loud::Loud() [c='c']
Loud::Loud() [c='a']
Loud::Loud() [c='b']
Gamma
~Gamma
Loud::~Loud() [c='b']
Loud::~Loud() [c='a']
Loud::~Loud() [c='c']