关于C ++中多继承的问题?

时间:2011-05-18 11:43:32

标签: c++ inheritance multiple-inheritance diamond-problem

我有以下代码:

#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;
}

执行时,程序会给出结果:

Address of faculty.Person::name: 0012FF20 // **Line 1**
Address of faculty.Student::name: 0012FF20 // **Line 2**
Address of faculty.Staff::name: 0012FF3F // **Line 3**

我不明白。为什么Line 1Line 2中的地址与Line 3不同,而学生和工作人员都从Person继承了姓名?

7 个答案:

答案 0 :(得分:13)

当您以这种方式执行多重继承时,您将获得祖父母类的两个副本。这是典型的dreaded diamond问题,您尝试这样做:


        Person
       /       \
     Student  Staff
       \       /
        Faculty

但是通过正常的继承,你实际上得到了这个:

    Person   Person
      |        |
     Student Staff
       \       /
        Faculty

所以在一个Faculty实例中真的有2个人,这意味着你会得到2个名字。

要获得上面第一个图表中的钻石,您需要使用virtual inheritance

class Staff : public virtual Person {
};
class Student : public virtual Person {
};

答案 1 :(得分:9)

使用常规多重继承,您可以获得共享基类的多个副本。如果您想要一个副本,请使用虚拟继承。

Wikipedia

中解释得很好
class Student : public virtual Person {
};
class Staff : public virtual Person {
};

会得到你的预期

答案 2 :(得分:2)

你是分别继承两个不同的类。

你应该使用virtual inheritance

答案 3 :(得分:1)

您遇到了经典的钻石继承问题。由于多重继承在C ++中的工作方式,name中实际上有{em>两个不同的Faculty副本。这通常可以通过使用这样的虚拟继承来解决,因此您只有一个Person实例及其成员:

class Student : public virtual Person {
};
class Staff : public virtual Person {
};

在这种情况下,我很确定你不想这样做。假设每个Faculty也是StudentStaff成员似乎是不合理的,因此您不应该以这种方式表示它。 Faculty始终是Staff似乎是合理的,因此您可以使用单继承来建模该关系。然后,如果需要,将Faculty中也需要的学生公共代码分解(自由函数或单独的类)。

答案 4 :(得分:0)

class Faculty继承了class Person的两个子对象,一个到class Student,另一个到class Staff

&faculty.Staff::name返回通过class Person派生的class Staff子对象的地址。

&faculty.Student::name返回通过class Person派生的class Student子对象的地址。

两者都是不同的子对象,因此地址不同。

答案 5 :(得分:0)

对于多重继承,派生类faculty有2个Person副本。第1到Student和第2到Staff

当您引用faculty.Person::name时,它会通过StudentStaff引用。这是一个模棱两可的情况,甚至不会用g ++编译。

在MSVC中,似乎由于Faculty首先继承Student,然后Staff,所以它将faculty.Person::name称为facutlty ==> Student ==> Person ==> name。这就是为什么前2行的输出相同而第3行不同的原因。

答案 6 :(得分:0)

有点偏离主题但......最有经验的开发人员避免多重继承。很难维持和充满危险。