Как реализовано множественное наследование в C ++? - PullRequest
21 голосов
/ 16 июня 2009

Одиночное наследование легко реализовать. Например, в C наследование может быть смоделировано как:

struct Base { int a; }
struct Descendant { Base parent; int b; }

Но при множественном наследовании компилятор должен расположить несколько родителей внутри вновь созданного класса. Как это сделать?

Проблема, с которой я сталкиваюсь, заключается в следующем: должны ли родители быть в АБ или БА или, может быть, даже как-то иначе? И потом, если я сделаю бросок:

SecondBase * base = (SecondBase *) &object_with_base1_and_base2_parents;

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

Ответы [ 7 ]

10 голосов
/ 16 июня 2009

Следующая статья от создателя C ++ описывает возможную реализацию множественного наследования:

Множественное наследование для C ++ - Бьярн Страуструп

5 голосов
/ 16 июня 2009

А потом, если я сделаю приведение:

SecondBase base = (SecondBase *) object_with_base1_and_base2_parents;

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

С невирусным наследованием это менее сложно, чем вы думаете - в момент компиляции приведения компилятор знает точную компоновку производного класса (в конце концов компилятор выполнил макет). Обычно все, что происходит, это фиксированное смещение (которое может быть нулевым для одного из базовых классов) добавляется / вычитается из указателя производного класса.

С вирулентным наследованием это может быть немного сложнее - это может включать захват смещения из vtbl (или аналогичного).

Книга Стэна Липпмана, "Внутри объектной модели C ++" содержит очень хорошее описание того, как этот материал может (и часто действительно работает).

5 голосов
/ 16 июня 2009

Была эта довольно старая статья MSDN о том, как она была реализована в VC ++.

1 голос
/ 16 июня 2009

Это интересная проблема, которая на самом деле не специфична для C ++. Вещи становятся более сложными, когда у вас есть язык с множественной диспетчеризацией и множественным наследованием (например, CLOS)

Люди уже отметили, что существуют разные способы решения проблемы. В этом контексте вам может быть интересно прочитать о мета-объектных протоколах (MOP) ...

1 голос
/ 16 июня 2009

Родители располагаются в указанном порядке:

class Derived : A, B {} // A comes first, then B

class Derived : B, A {} // B comes first, then A

Ваш второй случай обрабатывается в зависимости от компилятора. Один из распространенных методов - использовать указатели, размер которых превышает размер указателя платформы, для хранения дополнительных данных.

0 голосов
/ 17 июня 2009

Я выполнил простой эксперимент:

class BaseA { int a; };
class BaseB { int b; };
class Descendant : public BaseA, BaseB {};
int main() {
        Descendant d;
        BaseB * b = (BaseB*) &d;
        Descendant *d2 = (Descendant *) b;
        printf("Descendant: %p, casted BaseB: %p, casted back Descendant: %p\n", &d, b, d2);
}

Вывод:

Descendant: 0xbfc0e3e0, casted BaseB: 0xbfc0e3e4, casted back Descendant: 0xbfc0e3e0

Хорошо понимать, что статическое приведение не всегда означает «изменить тип, не касаясь содержимого». (Хорошо, когда типы данных не соответствуют друг другу, тогда будет также вмешательство в контент, но это другая ситуация IMO.)

0 голосов
/ 16 июня 2009

Это полностью зависит от компилятора, как это делается, но я полагаю, что это обычно делается через иерархическую структуру vtables.

...