Show Lecture.MultipleInheritance as a slide show.
CS253 Multiple Inheritance
Multiple Aspects
- A dog is man’s best friend. It is also:
- a guardian
- a walking companion
- a snuggler
- a yappy pain in the neck
- Similarly, I am a teacher, but I’m also a husband, a brother,
a musician, a comic book fan, a brony, a computer programmer,
a Detroit native, a U.S. citizen, etc.
- Nobody says that you have to be only one thing.
Multiple Inheritance
- In C++, unlike caffiene-based languages, multiple inheritance
is allowed.
- True, a Java class can inherit from several interfaces.
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
- However, Jack used to have two jobs:
- teaching Computer Science at CSU
- teaching aerobics at the Fort Collins Club
- Let’s change the program to reflect that.
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 │
└──────┘
- Two problems:
- Both
CSU_Employee
and FCC_Employee
have an employee_id
.
- There are two instances of the
Worker
class.
- Let’s fix the first problem, by qualifying the references
to
employee_id
.
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 │
└──────┘
- We still have the problem that there are two instances
of the
Worker
class.
- We solve this using virtual inheritance.
- This has nothing to do with virtual methods
except the word virtual.
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 │
└──────┘
- This hierarchy is called The Dreaded Diamond.
- Now, thanks to virtual inheritance,
we have only a single of the
Worker
class.
Worker
is a virtual base class of both
CSU_Employee
and FCC_Employee
.
- Therefore, when
CSU_Employee
and FCC_Employee
both
appear in an object hierarchy, Worker
only appears once.
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']
- Ctor order: base class, data members in order of declaration, ctor body
- Dtor order: the reverse of that.