В каком порядке вызываются конструкторы в C++?

«В каком порядке вызываются конструкторы в C++?» — вопрос из категории ООП, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Порядок вызова конструкторов в C++ жёстко фиксирован и происходит снизу вверх — от базовых частей к производным:

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

Деструкторы вызываются в строго обратном порядке.

Пример с множественным наследованием и полями:

#include <iostream>
#include <string>

class BaseA {
public:
    BaseA() { std::cout << "BaseA constructor.n"; }
};

class BaseB {
public:
    BaseB() { std::cout << "BaseB constructor.n"; }
};

class MemberX {
public:
    MemberX() { std::cout << "MemberX constructor.n"; }
};

class MemberY {
public:
    MemberY() { std::cout << "MemberY constructor.n"; }
};

// Наследование: BaseA, затем BaseB
// Объявление полей: MemberX mX, затем MemberY mY
class Derived : public BaseA, public BaseB {
    MemberX mX;
    MemberY mY;
public:
    // Список инициализации не влияет на порядок вызова конструкторов
    Derived() : mY(), mX() { // Порядок здесь роли не играет
        std::cout << "Derived constructor body.n";
    }
};

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

Вывод программы (порядок вызова):

BaseA constructor.    // 1. Базовый класс (первый в списке наследования)
BaseB constructor.    // 2. Базовый класс (второй в списке наследования)
MemberX constructor.  // 3. Поле (первое в объявлении класса)
MemberY constructor.  // 4. Поле (второе в объявлении класса)
Derived constructor body. // 5. Тело конструктора Derived