C ++ неявные и явные вызовы конструктора наследования

У меня есть вопрос о неявных и явных вызовах базового конструктора. Если у нас есть иерархия классов, как это:

class Person{
protected:
std::string m_name;
public:
Person(std::string& _name) : m_name(_name){std::cout << "A person is being constructed." << std::endl;}
};

class Baby : public Person{
private:
int m_no_of_nappies;
public:
Baby(std::string& _name, int& _no_of_nappies) : m_no_of_nappies(_no_of_nappies), Person(_name) {std::cout << "A baby is being constructed." << std::endl ;}

};

Согласно моим конспектам, звонок на «Малышку» в основном такой:

std::string babyname = "Robert";
int nappies = 5;

Baby baby(babyname, nappies);

Вызывает следующее:

  1. Поскольку явный вызов Person выполняется в списке инициализации Baby: Вызывается список инициализации ребенка, и no_of_nappies инициализируется.
  2. Затем выполняется вызов конструктора Person и Список инициализации человека называется. m_name инициализируется.
  3. Затем вызывается тело конструктора человека.
  4. Детское конструкторское тело наконец-то называется.

Это имеет смысл, однако, что, если бы были неявные обращения к конструктору по умолчанию родительского класса, например так:

class Vehicle{
protected:
int m_no_wheels;
public:
Vehicle() : m_no_wheels(0) { std::cout << "A vehicle is being constructed." << std::endl; }
};

class Bicycle : public Vehicle{
protected:
bool m_is_locked;
public:
Bicycle() : m_is_locked(false) { std::cout << "A bicycle is being constructed." << std::endl; }
};

Это та часть, в которой я не уверен. Мое лучшее предположение, что звонок Bicycle bike; В основном имеет следующий эффект:

  1. Неявный вызов сделан по умолчанию конструктору Vehicle из Bike. Перед тем, как вызывается список инициализации велосипеда.
  2. Поскольку транспортное средство не наследует от чего-либо, Список инициализации автомобиля называется где это инициализируется m_no_wheels в 0,
  3. Тело конструктора автомобиля называется.
  4. Возвращаемся на велосипед и теперь его список инициализации называется, инициализация m_is_locked в false,
  5. Тело конструктора Байка называется.

Может ли кто-нибудь объяснить, правильны ли мои аргументы в пользу неявного вызова?

Основное отличие, на мой взгляд, заключается в том, что при явной ссылке на базовый конструктор список инициализации дочернего класса всегда вызывается первым, чтобы вызвать этот базовый конструктор — однако при неявном вызове самый верхний родительский класс список инициализации всегда выполняется первым.

Спасибо и высоко ценится!

Редактировать: Я спрашиваю конкретно, если порядок меняется, в зависимости от неявного или явного вызова родительского класса.

4

Решение

Порядок инициализации баз и членов указан в [class.base.init] / 11, и вы можете найти резюме здесь: http://en.cppreference.com/w/cpp/language/initializer_list#Initialization_order

Порядок инициализаторов элементов в списке не имеет значения: фактический порядок инициализации следующий:

  1. Если конструктор предназначен для самого производного класса, виртуальные базовые классы инициализируются в том порядке, в котором они появляются при обходе слева направо в объявлениях базового класса в глубину (слева направо относится к появлению в base списки спецификаторов)
  2. Затем прямые базовые классы инициализируются в порядке слева направо по мере их появления в списке базовых спецификаторов этого класса.
  3. Затем нестатические члены-данные инициализируются в порядке объявления в определении класса.
  4. Наконец, тело конструктора выполняется

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

Порядок инициализации устанавливается в камне до того, как были определены какие-либо конструкторы; список инициализатора конструктора влияет только как базы и члены инициализируются, а не порядок, в котором они инициализируются.

Так как Person является основой Babyвсегда инициализируется раньше Babyчлен m_no_of_nappies, В рамках инициализации Person, его собственные члены инициализируются, а затем выполняется его тело конструктора. После Personтело конструктора возвращается, то m_no_of_nappies инициализируется. (Разрушение всегда происходит в обратном порядке.) Vehicle также основа Bicycleи инициализируется первым; так как нет мем-инициализатор для этого вызывается конструктор по умолчанию.

3

Другие решения

§12.6.2 определяет, как вещи инициализируются:

Порядок инициализаторов членов в списке не имеет значения: фактический
порядок инициализации следующий:

  • Если конструктор предназначен для самого производного класса, виртуальные базовые классы
    инициализируются в том порядке, в котором они появляются по глубине
    обход слева направо объявлений базового класса (слева направо
    относится к появлению в списках базовых спецификаторов)
  • Тогда прямая база
    классы инициализируются в порядке слева направо, как они появляются в этом
    список базовых спецификаторов класса
  • Тогда нестатические члены данных
    инициализируется в порядке объявления в определении класса.
  • В заключение,
    тело конструктора выполняется (Примечание: если порядок инициализации
    контролировалось появлением в списках инициализатора членов
    различные конструкторы, то деструктор не сможет обеспечить
    что порядок уничтожения является обратным порядку
    строительство)

Подведем итог для вашего случая (и оставив в стороне виртуальные функции):

  1. Базовые классы в порядке заявленного наследования
  2. Члены в порядке декларации

таким образом порядок в списке инициализатора конструктора не влияет.

В первом случае вы ошибаетесь в этом пункте: Person это базовый класс Baby и инициализируется раньше m_no_of_nappies


Редактировать:
Ваш вопрос

Baby вызывает Person из своего списка инициализации, поэтому первое, что бросается в глаза, это список инициализации Baby?

[Class.base.init] / 10 Вероятно, это то, что вы ищете: вы на самом деле не «вызываете» конструктор базового класса (при условии, что нет делегирования), он вызывается компилятором для вас при инициализации производного объекта.

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

Причиной игнорирования порядка инициализаторов является сохранение обычного порядка FIFO вызовов конструктора и деструктора. Разрешение двум конструкторам использовать разные порядки инициализации баз и членов ограничило бы реализации использованием более динамичных и более дорогих стратегий.

Взято из https://stackoverflow.com/a/24287946/1938163

И наконец

выполняется неявный вызов базового класса (который выполняет компилятор) до списка инициализации Bicycle или после?

Перед остальной частью инициализации класса члена как в §12.6.2.

1

По вопросам рекламы [email protected]