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

Как виртуальное наследование С++ реализовано в компиляторах?

Как компиляторы реализуют виртуальное наследование?

В следующем коде:

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

class B : public virtual A {
  public:
    B() : A(1) {}
};

class C : public B {
  public:
    C() : A(3), B() {}
};

Создает ли компилятор два экземпляра функции B::ctor, один без вызова A(1) и один с ним? Поэтому, когда B::constructor вызывается из конструктора производного класса, используется первый экземпляр, в противном случае второй.

4b9b3361

Ответ 1

Это зависит от реализации. GCC (см. этот вопрос), например, испустит два конструктора: один с вызовом A(1), другой без него.

B1()
B2() // no A

Когда B строится, вызывается "полная" версия:

B1():
    A(1)
    B() body

Когда C построено, вместо него вызывается базовая версия:

C():
    A(3)
    B2()
       B() body
    C() body

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

Ответ 2

Компилятор не создает другого конструктора B, но игнорирует A(1). Поскольку A фактически унаследован, он сначала создается с его конструктором по умолчанию. И поскольку он уже сконструирован при вызове B(), часть A(1) игнорируется.

Изменить. Я пропустил часть A(3) в списке инициализации конструктора C. Когда используется виртуальное наследование, только самый производный класс инициализирует виртуальные базовые классы. Таким образом, A будет построен с A(3), а не его конструктором по умолчанию. Остальное остается - любые инициализации A промежуточным классом (здесь B) игнорируются.

Отредактируйте 2, пытаясь ответить на фактический вопрос относительно реализации вышеперечисленного:

В Visual Studio (по крайней мере, 2010) вместо двух реализаций B() используется флаг. Поскольку B фактически наследует от A, прежде чем он называет конструктор A, флаг проверяется. Если флаг не установлен, вызов A() пропускается. Затем в каждом классе, полученном из B, флаг reset после инициализации A. Тот же механизм используется для предотвращения C от инициализации A, если он является частью некоторого D (если D наследует от C, D будет инициализировать A).

Ответ 3

Itanium С++ ABI - полезный ресурс для всех вопросов, таких как "как это можно реализовать с помощью компиляторов С++".

В частности 5.1.4 Другие специальные функции и сущности перечисляют различные специальные функции-члены для разных целей:

<ctor-dtor-name> ::= C1   # complete object constructor
             ::= C2   # base object constructor
             ::= C3   # complete object allocating constructor
             ::= D0   # deleting destructor
             ::= D1   # complete object destructor
             ::= D2   # base object destructor

Раздел 1.1 Определения полезен (но не завершен):

деструктор базового объекта класса T

Функция, которая запускает деструкторы для нестатических членов данных из T и не виртуальных прямых базовых классов T.

полный деструктор объекта класса T

Функция, которая в дополнение к действиям, требуемым для деструктора базового объекта, запускает деструкторы для виртуальных базовых классов от T.

удаление деструктора класса T

Функция, которая в дополнение к действиям, требуемым для полного деструктора объекта, вызывает соответствующую функцию освобождения (т.е. оператор delete) для T.

Из этих определений цель полного конструктора объектов и конструктора базового объекта очевидна.

Ответ 4

Я предлагаю вам прочитать некоторые документы. Эти два действительно интересны, особенно первые, поскольку он исходит от отца С++:

[1] Бьярне Страуструп. Множественное наследование для С++. Пользователи C/С++ Journal, май 1999.

[2] J. Templ. Системный подход к многократной реализации наследования. ACM SIGPLAN, Volume 28, No. 4 April 1993.

Я использовал их в качестве основных ссылок, проводя семинар (как студент) по множественному наследованию в своем университете.

Ответ 5

Как упоминалось ранее, это зависит от реализации компилятора.

Но, как правило, каждый раз, когда программист добавляет новый метод, он сохраняется в коде, даже если есть другой метод с тем же идентификатором. в другом месте ( "overriden" или "overloaded" ).

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

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

Не беспокойтесь о производительности, компилятор не дублирует код для методов.