CS253: Software Development with C++

Spring 2021

Multiple Inheritance

Show Lecture.MultipleInheritance as a slide show.

CS253 Multiple Inheritance


Vishnu and his avatars

Multiple Aspects

Multiple Inheritance

Space Considerations

Example

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

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

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

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

Example

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

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

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

Instructor me;
Citizen *c = &me;
c->vote();
Stupid electoral college!

Example

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

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

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

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

No, virtual doesn’t help

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

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

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

Instructor me;
Citizen *c = &me;
c->vote();      // ok
c->lecture();   // fails even with virtual
c->relax();     // fails even with virtual
c.cc:16: error: 'struct main()::Citizen' has no member named 'lecture'
c.cc:17: error: 'struct main()::Citizen' 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 │
	   └────────────┘

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