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. как явный вызов к человеку производится в списке инициализации ребенка: список инициализации ребенка вызывается, и no_of_nappies инициализируется.

  2. Далее производится вызов конструктора человека и вызывается список инициализации человека. инициализируется 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. выполняется неявный вызов конструктора транспортного средства по умолчанию из Bike. перед вызовом списка инициализации велосипеда.

  2. поскольку транспортное средство ни от чего не наследуется, список инициализации транспортного средства называется , где он инициализирует m_no_wheels до 0.


  3. тело конструктора транспортного средства называется.

  4. возвращаемся к велосипеду и теперь его инициализационный список называется , инициализируя m_is_locked к false.


  5. тело конструктора велосипеда называется.


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

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

Спасибо вы, и очень благодарны!



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

824   2  

2 ответов:

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

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

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

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

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

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

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

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

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

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

  1. базовые классы в порядке объявленного наследования
  2. члены в порядке объявления

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

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


Править: Ваш вопрос

Ребенок вызывает человека из своего списка инициализации, следовательно, первое, что попадает в список инициализации ребенка?

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

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

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

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

И, наконец,

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

Перед остальными инициализациями класса-члена, как в §12.6.2.

Comments

    Ничего не найдено.