Как C ++ виртуальное наследование реализовано в компиляторах? - PullRequest
22 голосов
/ 09 сентября 2011

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

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

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 вызывается из конструктора производного класса, используется первый экземпляр, в противном случае - второй.

Ответы [ 5 ]

8 голосов
/ 09 сентября 2011

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

B1()
B2() // no A

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

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

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

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

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

8 голосов
/ 09 сентября 2011

Компилятор не создает другого конструктора 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, флаг сбрасывается после инициализации A. Тот же механизм используется для предотвращения инициализации C A, если он является частью некоторого D (если D наследуется от C, D инициализирует A).

3 голосов
/ 05 августа 2012

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

В частности 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.

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

3 голосов
/ 09 сентября 2011

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

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

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

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

0 голосов
/ 09 сентября 2011

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

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

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

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

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

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