Подтвердить что ты не робот

Как реализовано многократное наследование С++?

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

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

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

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

SecondBase * base = (SecondBase *) &object_with_base1_and_base2_parents;

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

4b9b3361

Ответ 3

И затем, если я сделаю бросок:

SecondBase base = (SecondBase *) object_with_base1_and_base2_parents;

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

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

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

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

Ответ 4

Родители расположены в том порядке, в котором они указаны:

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

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

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).