Путаница относительно порядка вызова конструктора и виртуального базового класса - PullRequest
0 голосов
/ 09 октября 2018

order-of-call.cpp

#include <iostream>

class A
{
public:
    A()
    {
        std::cout << "A" ;
    }

};

class B: public A
{
public:
    B()
    {
        std::cout << "B" ;
    }
};

class C: virtual public A
{
public:
    C()
    {
        std::cout << "C" ;
    }

};

class D: public B, public C
{
public:
    D()
    {
        std::cout << "D" ;        
    }

};


int main()
{
    D d;
    return 0;
}

Компиляция

g++ order-of-call.cpp -std=c++11

Выход

AABCD

Почему два A вместе выводятся?Я ожидал что-то вроде ABACD.Но если я изменю порядок наследования следующим образом class D: public C, public B, результат будет таким, как ожидалось ACABD.Является ли заказ частью стандарта или является чем-то конкретным для g ++.

Ответы [ 4 ]

0 голосов
/ 09 октября 2018

Если вы добавите отсутствующее виртуальное здесь:

class B: virtual public A

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

Тогда вывод будет простым ABCD илив случае изменения порядка

class D: public C, public B

Вывод:

ACBD

Это соответствует стандарту, и это поведение не зависит от используемого компилятора

Однако, если вам нужно иметь виртуальный только для класса C.В любом случае виртуальный подрядчик будет вызываться первым, опять же в соответствии со стандартом:

ИСО / МЭК JTC1 SC22 WG21 N 3690

ВНе делегирующий конструктор, инициализация происходит в следующем порядке:

  • Первый и только для конструктора самого производного класса (1.8), виртуальные базовые классы являютсяинициализируется в том порядке, в котором они появляются при обходе слева направо по глубине направленного ациклического графа базовых классов, где «слева направо» - это порядок появления базовых классов в базовом спецификаторе производного класса-list.

  • Затем прямые базовые классы инициализируются в порядке объявления по мере их появления в списке базовых спецификаторов (независимо от порядка mem-initializer).

  • Затем не статические члены данных инициализируются в том порядке, в котором они были объявлены в определении класса (опять же, независимо от порядка mem-инициализаторов).
  • Наконец,составной оператор тела конструктора выполнен.
0 голосов
/ 09 октября 2018

Это представляется весьма актуальным: https://isocpp.org/wiki/faq/multiple-inheritance#mi-vi-ctor-order,, в частности:

Самые первые выполняемые конструкторы - это виртуальные базовые классы в любой точке иерархии.

Послевсе виртуальные конструкторы базового класса закончены, порядок построения обычно от базового класса к производному классу.Поэтому, если класс D наследует умножение от B1 и B2, сначала выполняется конструктор для B1, затем конструктор для B2, а затем конструктор для D.

Обратите внимание, что порядок B1, а затем B2 (или B1a, затем B1b)определяется порядком, в котором базовые классы появляются в объявлении класса.

Таким образом, первый A, который вы видите, всегда через виртуальное наследование C->A.Отдых обычно в глубину слева направо.

0 голосов
/ 09 октября 2018

Последовательность построения - это сначала виртуальные базы, а затем - построение не виртуальных баз в глубоком порядке слева направо (рекурсивно).

Итак, при построении D виртуальныйОснова A класса C будет построена первой (следовательно, выводится первое 'A').Затем начинается построение B в пределах D, которое сначала создает не виртуальный A (вывод второй 'A'), который является не виртуальной базой B, а затем вызывает конструктор B (вывод 'B').Затем вызывается конструктор C - его база A является виртуальной, поэтому не создается снова, а затем вызывается конструктор C (вывод 'C').Наконец, вызывается конструктор 'D' (вывод 'D').

0 голосов
/ 09 октября 2018

Это имеет смысл, поскольку виртуальный базовый класс создается перед не виртуальными базовыми классами.Так что в вашем случае это: virtual A, non-virtual A, BCD.Если вы измените порядок наследования, это будет virtual A, C, non-virtual A, BD.Оформить заказ: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.cbclx01/cplr389.htm

Порядок инициализации класса следующий:

  1. Конструкторы виртуальных базовых классов выполняются в порядке их появления в базовом списке.
  2. Конструкторы базовых классов, не являющихся виртуальными, выполняются в порядке объявления.
  3. Конструкторы членов класса выполняются в порядке объявления (независимо от их порядка в списке инициализации).
  4. Тело конструктора выполнено.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...