CS253: Software Development with C++

Fall 2021

Multiple Inheritance

Show Lecture.MultipleInheritance as a slide show.

CS253 Multiple Inheritance

Multiple Aspects

Multiple Inheritance

Space Considerations

Example

struct Manager {
    void nag() { cout << "Grade those labs!\n"; }
};

struct Teacher {
    void lecture() { cout << "Avoid endl!\n"; }
};

struct Instructor : public Manager, public Teacher {
    void relax() { cout << "Read a comic book.\n"; }
};

Instructor me;
me.nag();       // method inherited from Manager
me.lecture();   // method inherited from Teacher
me.relax();     // method of Instructor
Grade those labs!
Avoid endl!
Read a comic book.

Example

struct Manager {
    void nag() { cout << "Grade those labs!\n"; }
};

struct Teacher {
    void lecture() { cout << "Avoid endl!\n"; }
};

struct Instructor : public Manager, public Teacher {
    void relax() { cout << "Read a comic book.\n"; }
};

Instructor me;
Manager *m = &me;
m->nag();
Grade those labs!

Example

struct Manager {
    void nag() { cout << "Grade those labs!\n"; }
};

struct Teacher {
    void lecture() { cout << "Avoid endl!\n"; }
};

struct Instructor : public Manager, public Teacher {
    void relax() { cout << "Read a comic book.\n"; }
};

Instructor me;
Manager *m = &me;
m->nag();       // ok
m->lecture();   // Not a method of Manager
m->relax();     // Not a method of Manager
c.cc:16: error: 'struct main()::Manager' has no member named 'lecture'
c.cc:17: error: 'struct main()::Manager' has no member named 'relax'

No, virtual doesn’t help

struct Manager {
    virtual void nag() { cout << "Grade those labs!\n"; }
};

struct Teacher {
    virtual void lecture() { cout << "Avoid endl!\n"; }
};

struct Instructor : public Manager, public Teacher {
    virtual void relax() { cout << "Read a comic book.\n"; }
};

Instructor me;
Manager *m = &me;
m->nag();       // ok
m->lecture();   // fails even with virtual
m->relax();     // fails even with virtual
c.cc:16: error: 'struct main()::Manager' has no member named 'lecture'
c.cc:17: error: 'struct main()::Manager' has no member named 'relax'

Ambiguity

class Base1 { public: int x=11; };
class Base2 { public: int x=22; };
class Derived : public Base1, public Base2 {
  public:
    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: 'int Base2::x'
c.cc:1: note:                 'int Base1::x'

Resolving Ambiguity

class Base1 { public: int x=11; };
class Base2 { public: int x=22; };
class Derived : public Base1, public Base2 {
  public:
    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

Multiple Levels

struct Worker {
    Worker() { clog << "Worker\n"; }
    int ss_num;
};

struct CSU_Employee : Worker {
    int employee_id;
};

struct Instructor : CSU_Employee {
    int comics;
};

int main() {
    Instructor me;
    me.comics = 1000003;
    me.employee_id = 812345678;
    me.ss_num = 362269871;
    cout << me.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 Instructor : CSU_Employee { int comics; };

int main() {
    Instructor me;
    me.comics = 1000003;
    me.employee_id = 812345678;
    me.ss_num = 362269871;
    cout << me.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 Instructor : CSU_Employee, FCC_Employee { int comics; };

int main() {
    Instructor me;
    me.comics = 1000003;        // OK
    me.employee_id = 3;         // ambiguous
    me.employee_id = 812345678; // ambiguous
    me.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 │
└──────────────┘  └──────────────┘
          ╲             ╱
           ╲           ╱
            ╲         ╱
             ╲       ╱
	   ┌────────────┐
	   │ Instructor │
	   └────────────┘

Consider the class hierarchy:

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

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 Instructor : CSU_Employee, FCC_Employee { int comics; };

int main() {
    Instructor me;
    me.comics = 1000003;
    me.FCC_Employee::employee_id = 3;
    me.CSU_Employee::employee_id = 812345678;
    me.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 │
└──────────────┘  └──────────────┘
          ╲             ╱
           ╲           ╱
            ╲         ╱
             ╲       ╱
	   ┌────────────┐
	   │ Instructor │
	   └────────────┘

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 Instructor : CSU_Employee, FCC_Employee { int comics; };

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

New Inheritance Hierarchy

	  ┌──────────────┐
	  │    Worker	 │
	  └──────────────┘
             ╱       ╲
            ╱         ╲
           ╱           ╲
          ╱             ╲
┌──────────────┐  ┌──────────────┐
│ CSU_Employee │  │ FCC_Employee │
└──────────────┘  └──────────────┘
          ╲             ╱
           ╲           ╱
            ╲         ╱
             ╲       ╱
	   ┌────────────┐
	   │ Instructor │
	   └────────────┘

Introducing class Loud

I have a class Loud, which announces every method:

int main() {
    Loud a, b('g');
    a = ++b;
}
Loud::Loud()
Loud::Loud() [c='g']
Loud::operator++() [c='h']
Loud::operator=(const Loud &) [c='h']
Loud::~Loud() [c='h']
Loud::~Loud() [c='h']

Order of construction

class Gamma : public Loud {
  public:
    Gamma() : beta('B'), Loud('L'), alpha('A') {
            clog << __func__ << '\n';
    };
    ~Gamma() { clog << __func__ << '\n'; }
  private:
    Loud alpha, beta;
};

Gamma g;
Loud::Loud() [c='L']
Loud::Loud() [c='A']
Loud::Loud() [c='B']
Gamma
~Gamma
Loud::~Loud() [c='B']
Loud::~Loud() [c='A']
Loud::~Loud() [c='L']

It makes sense

class FullName {
  public:
    FullName(string f, string l) {
        first = f;
        last = l;
    }
  private:
    string first, last;
};

Destructors

class TempFile {
  public:
    TempFile() {
        path = …;
        create that temporary file;
    }
    ~TempFile() {
        remove(path);
    }
  private:
    string path;
};

delete