Question about multi-inheritance in C++?
-
15-11-2019 - |
Pregunta
I have the following code:
#include "stdafx.h"
#include <iostream>
#include <conio.h>
using namespace std;
#define MNAME 30
class Person {
public:
char name[MNAME + 1];
};
class Student : public Person {
};
class Staff : public Person {
};
class Faculty : public Student, public Staff {
};
int _tmain(int argc, _TCHAR* argv[])
{
Faculty faculty;
cout << "Address of faculty.Person::name: " << &faculty.Person::name << endl;
cout << "Address of faculty.Student::name: " << &faculty.Student::name << endl;
cout << "Address of faculty.Staff::name: " << &faculty.Staff::name << endl;
getch();
return 0;
}
When executed, the program gives the results:
Address of faculty.Person::name: 0012FF20 // **Line 1**
Address of faculty.Student::name: 0012FF20 // **Line 2**
Address of faculty.Staff::name: 0012FF3F // **Line 3**
I don't get it. Why the address in Line 1
and Line 2
is different from Line 3
, while both Student and Staff inherits name from Person?
Solución
With regular multiple inheritance you get multiple copies of shared base classes. If you want one copy, use virtual inheritance.
Explained well in Wikipedia
class Student : public virtual Person {
};
class Staff : public virtual Person {
};
Will get you what you expected
Otros consejos
When you do multiple inheritance this way you get two copies of the grandparent class. This is the classic dreaded diamond problem, where you try to do this:
Person / \ Student Staff \ / Faculty
but through normal inheritance you actually get this:
Person Person | | Student Staff \ / Faculty
So there's really 2 Person's in an instance of Faculty, meaning youll get 2 names.
To get the diamond in the first diagram above, you want to use virtual inheritance.
class Staff : public virtual Person {
};
class Student : public virtual Person {
};
you are inheriting from the two different classes separately.
you should use virtual inheritance
You've encountered the classic diamond inheritance problem. Due to the way multiple inheritance work in C++ there are actually two distinct copies of name
in Faculty
. This can typically be solved by using virtual inheritance like this, so you only have one instance of Person
and its members:
class Student : public virtual Person {
};
class Staff : public virtual Person {
};
I'm pretty sure in this case however you don't want to do this. It doesn't seem reasonable to assume that every Faculty
is also a Student
AND a Staff
member, so you shouldn't represent it in this way. It seems plausible that a Faculty
would always be a Staff
, so you could use single inheritance to model that relationship. Then if needed, factor out (into free functions or a separate class) the common code from student that's also needed in Faculty
.
class Faculty
inherits two sub objects of class Person
, one through class Student
and another through class Staff
.
&faculty.Staff::name
returns address of the sub object of class Person
derived through class Staff
.
&faculty.Student::name
returns address of the sub object of class Person
derived through class Student
.
Both are different sub objects and hence the different address.
For multiple inheritance your derived class faculty
has 2 copies of Person
. 1st through Student
and 2nd through Staff
.
When you refer faculty.Person::name
, it refers either via Student
or via Staff
. This is an ambiguous situation and even will not compile with g++.
In MSVC, it seems that since Faculty
inherits Student
first and then Staff
, it's referring faculty.Person::name
as facutlty ==> Student ==> Person ==> name
. That's why output of first 2 lines are same and 3rd line is different.
A bit off topic but....Most experienced developers avoid multiple inheritance. It is difficult to maintain and fraught with peril.