Вопрос о множественном наследовании в C ++? - PullRequest
8 голосов
/ 18 мая 2011

У меня есть следующий код:

#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 1 и Line 2 отличается от Line 3, в то время как ученик и персонал наследуют имя от Person?

Ответы [ 7 ]

13 голосов
/ 18 мая 2011

Когда вы делаете множественное наследование таким образом, вы получаете две копии класса прародителя. Это классическая проблема страшный бриллиант , где вы пытаетесь сделать это:


        Person
       /       \
     Student  Staff
       \       /
        Faculty

но через обычное наследование вы действительно получаете это:

    Person   Person
      |        |
     Student Staff
       \       /
        Faculty

Таким образом, в экземпляре факультета действительно 2 человека, то есть вы получите 2 имени.

Чтобы получить ромб на первой диаграмме выше, вы хотите использовать виртуальное наследование .

class Staff : public virtual Person {
};
class Student : public virtual Person {
};
9 голосов
/ 18 мая 2011

При регулярном множественном наследовании вы получаете несколько копий общих базовых классов.Если вам нужна одна копия, используйте виртуальное наследование.

Хорошо объяснено в Википедии

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

Получит то, что вы ожидали

2 голосов
/ 18 мая 2011

вы наследуете от двух разных классов отдельно.

вы должны использовать виртуальное наследование

1 голос
/ 18 мая 2011

Вы столкнулись с классической проблемой наследования алмазов.Благодаря тому, что множественное наследование работает в C ++, на самом деле существует две отдельные копии name в Faculty.Обычно это можно решить с помощью виртуального наследования, как это, поэтому у вас есть только один экземпляр Person и его членов:

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

Я уверен в этом случае, однако вы не хотитесделай это.Не кажется разумным предполагать, что каждый Faculty также является Student И Staff членом, поэтому вы не должны представлять его таким образом.Кажется вероятным, что Faculty всегда будет Staff, поэтому вы можете использовать одиночное наследование для моделирования этих отношений.Затем, если необходимо, выделите (в свободные функции или отдельный класс) общий код от студента, который также необходим в Faculty.

0 голосов
/ 18 мая 2011

Немного не по теме, но .... Большинство опытных разработчиков избегают множественного наследования.Это сложно поддерживать и чревато опасностью.

0 голосов
/ 18 мая 2011

Для множественного наследования ваш производный класс faculty имеет 2 копии Person. С 1 по Student и со 2 по Staff.

Когда вы ссылаетесь на faculty.Person::name, оно ссылается либо через Student, либо через Staff. Это неоднозначная ситуация и даже не будет компилироваться с g ++.

В MSVC кажется, что, поскольку Faculty сначала наследует Student, а затем Staff, он обозначает faculty.Person::name как facutlty ==> Student ==> Person ==> name. Вот почему вывод первых 2 строк одинаков, а 3-й строки разный.

0 голосов
/ 18 мая 2011

class Faculty наследует два подобъекта class Person, один через class Student, а другой через class Staff.

&faculty.Staff::name возвращает адрес подобъекта class Person, полученного из class Staff.

&faculty.Student::name возвращает адрес подобъекта class Person, полученный из class Student.

Оба являются разными подобъектами и, следовательно, разными адресами.

...