Следуя порядку выполнения конструктора в C ++ - PullRequest
0 голосов
/ 02 июля 2019

У меня есть следующая программа в C++:

class A {
public :
    A(){
        cout << "A::A()" << endl;
    }
    A( int x){
        cout << "A::A(int)" << endl;
    }
};

class B : public A {
public :
    B(){
        cout << "B::B()" << endl;
    }
    B( int x){
        cout << "B::B(int)" << endl;
    }
};

class C : public virtual B {
    public :
    C(){
        cout << "C::C()" << endl;
    }
    C( int x){
        cout << "C::C(int)" << endl;
    }
};

class D : public B {
public :
    D(){
        cout << "D::D()" << endl;
    }
    D( int x) : B(x){
        cout << "D::D(int)" << endl;
    }
};


class E : public C, public virtual
D, public virtual B {
public :
    E(){
        cout << "E::E()" << endl;
    }
    E( int x) : D(x){
        cout << "E::E(int)" << endl;
    }
};


int main() {
    E(5);
    return 0;
}

Я пытаюсь понять, что будет напечатано.Я постараюсь объяснить, как я вижу ситуацию.Сначала мы называем E(5).В конструкторе E(int) мы имеем следующий синтаксис: E(int x) : D(x), тогда должен вызываться конструктор D(x) (а не конструкторы унаследованных классов class E : public C, public virtual D, public virtual B, то есть только D(x) следует вызывать без C(),D(),B()),В классе D нам нужно назвать B().Эту часть я не понимаю - мы вызвали конструктор D(int), который также имеет синтаксис: D(int x) : B(x), поэтому должен вызываться B(x), а не B().Если нам нужно использовать конструктор B(), даже если вызванный конструктор B(x), то почему, когда мы вызывали конструктор E(5), мы не выполнили E().

Пока что я будупринять, что B() должен быть вызван первым.Затем мы вызываем конструктор A() и выполняем его, а затем B().Когда мы добираемся до D(x), мы вызываем B(x), а затем A(), поэтому до сих пор мы печатаем:

A::A()
B::B()
A::()
B::B(int)
D::D(int)

Теперь нам нужно вернуться к E(5) и выполнить его, но по какой-то причинеC() конструктор вызывается и я не понимаю почему.Мы сказали, что если у нас есть синтаксис E(int x) : D(x), то мы выполняем только D(x).Каким правилам / алгоритму следовать в этих вопросах?

РЕДАКТИРОВАТЬ : Ожидаемый результат:

A::A()
B::B()
A::A()
B::B(int)
D::D(int)
C::C()
E::E(int)

Вывод с моей точки зрения:

A::A() // not fully agree
B::B() // not fully agree
A::A()
B::B(int)
D::D(int)
E::E(int)

1 Ответ

0 голосов
/ 02 июля 2019

В соответствии со стандартом C ++ (C ++ 17, 15.6.2 Инициализация баз и членов, стр.13)

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

(13.1) - Во-первых, и только для конструктора самого производного класса (6.6.2), виртуальные базовые классы инициализируются в порядке, в котором они появляются в обход слева направо в глубинуориентированного ациклического графа базовых классов , где «слева направо» - порядок появления базовых классов в списке базовых спецификаторов производного класса.

Для этогообъявление класса

class E : public C, public virtual
D, public virtual B {
public :
    E(){
        cout << "E::E()" << endl;
    }
    E( int x) : D(x){
        cout << "E::E(int)" << endl;
    }
};

и это явное функциональное выражение преобразования

E(5);

конструктор первой глубины является конструктором по умолчанию для виртуальной общедоступной B.

Так что по умолчаниюконструктор class B сначала вызывается

A::A()
B::B()

, затем конструктор преобразования D( int ) называется

A::A()
B::B(int)
D::D(int)

и, наконец, конструктор не виртуальной базы class Cэто окlled

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

C::C()

затем управление (после инициализации нестатических элементов данных) передается в тело constructor E и выводится сообщение

E::E(int)

.

Учтите, что конструкторы виртуально унаследованных классов вызываются первыми и только один раз.Таким образом, конструктор класса C не будет вызывать конструктор класса B, потому что соответствующий конструктор уже был вызван до вызова конструктора C.

...