Внутренний механизм виртуального наследования

Пример кода на C ++:

class A {
public:
A(int) {}
};

class B : public virtual A {
public:
B(int b) : A(b) {}
};

class C : virtual public A {
public:
C(int c) : A(c) {}
};

class D : public B, public C {
public:
D() : B(1), C(2){};
};

Это типичный код (решение) для алмазной проблемы. Я знаю, почему используется виртуальное ключевое слово. Но внутренний механизм, с помощью которого компилятор решает эту проблему, мне неизвестен. Теперь я столкнулся с двумя различными теориями об указанном механизме, которые изложены ниже.

  1. Когда класс наследуется с виртуальным ключевым словом, компилятор добавляет указатель виртуальной базы в производный класс. Я проверил размер производного класса и да, он включает в себя размер дополнительного указателя. Но я не знаю, на что он указывает и как он работает, когда член класса A упоминается в классе D в приведенном выше примере.

  2. Для каждого из конструкторов компилятор создает две версии каждого определения, предоставленного программистом. Узнал от эта ссылка
    например в приведенном выше коде.
    Компилятор сгенерирует 2 версии конструктора C

     C(int){}           // Version1
    
    C(int):A(int){}    // Version2
    

    И две разные версии конструктора B

     B(int){}           // Version1
    
    B(int):A(int){}    // Version2
    

    Поэтому, когда D построен, компилятор сгенерирует любой из следующих кодов

    D() : B(), C(2) {}  // Version1
    
    D() : B(1), C() {}  // Version2
    

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

Пожалуйста, помогите мне понять внутренний механизм.

2

Решение

Обычное использование (не указано ни в одном стандарте!) — сначала создать экземпляр виртуально унаследованного объекта и поместить указатель на него в vtables. Итак, вот что происходит:

  • создание А: ничего особенного
  • создание B: a A строится и ссылка на него добавляется в B vtable
  • Создание D: сначала создается A, затем B и C, и каждый из них содержит ссылку на A в своей таблице. Это позволяет, когда вы получаете указатель на объект D, приводите его к указателю на B и C, и каждый из указателей все равно будет знать, где находятся его члены A.

Но это не более чем теоретический ответ о том, что может быть реализацией. Я не говорю, что фактическая реализация (скажем, gcc, clang или microsoft vc) следует именно этому. Но вы можете использовать его, например, если вам нужно имитировать виртуальное наследование на простом языке Си.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]