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?

¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top