CS253: Software Development with C++

Spring 2023

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.

Manager and Teacher are independent—no is-a relationship either way.

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 = 362009871;
    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 = 362009871;
    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 = 362009871;      // 🦡 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

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; };
┌──────────────┐  ┌──────────────┐
│     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 = 362009871;  // 🦡 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 = 362009871;
}
Worker

New Inheritance Hierarchy

	  ┌──────────────┐
	  │    Worker	 │
	  └──────────────┘
             ⠌       ⠡
            ⠌         ⠡
           ⠌           ⠡
          ⠌             ⠡
┌──────────────┐  ┌──────────────┐
│ CSU_Employee │  │ FCC_Employee │
└──────────────┘  └──────────────┘
          ⠡             ⠌
           ⠡           ⠌
            ⠡         ⠌
             ⠡       ⠌
	   ┌────────────┐
	   │ Instructor │
	   └────────────┘

class Loud

Remember class Loud, which announces every method:

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

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() {
        // fill the path variable
        // create temporary file
    }
    ~TempFile() {
        remove(path);
    }
  private:
    string path;
};

delete