Правильный ответ - {мусор} {мусор} 5.



Список инициализации в конструкторе - вещь дельная, полезная и иногда без нее никуда. Но с ним есть один неприятный момент - все может пойти по шляпе, если не соблюдать одно правило. "Порядок инициализации полей в списке инициализации конструктора должен совпадать с порядком следования полей в описании класса". Но вот не все знают, что же будет, если это правило нарушить. Поэтому давайте разбираться.



Для начала приведу цитату из стандарта:



In a non-delegating constructor, initialization proceeds in the following order:



- First, and only for the constructor of the most derived class, virtual base classes

are initialized in the order they appear on a depth-first left-to-right traversal of the

directed acyclic graph of base classes, where “left-to-right” is the order of appearance

of the base classes in the derived class base-specifier-list.



- Then, direct base classes are initialized in declaration order as they appear in the 

base-specifier-list(regardless of the order of the mem-initializer)



- Then, non-static data members are initialized in the order they were declared in the

class definition (again regardless of the order of the mem-initializer()



- Finally, the compound-statement of the constructor body is executed.





Теперь разжуем эту сухую писанину в мягкую кашицу для лучшего усвоения.



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



Далее инициализируются нестатические поля класса в порядке, в котором они объявлены в определении класса, и который никак не зависит от списка инициализации конструктора.



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



Какие из этого выводы?



Самый главный - поведение абсолютно определено стандартом. Здесь нет никакого UB! Список инициализации лишь задает способ инициализации, но никак не влияет на порядок. Порядк определяется только порядком следования полей в описании класса(сверху вниз).



То есть в нашем примере в начале будет инициализировать b, потом a и, наконец, c. И не смотря на то, что в списке инициализации c стоит первым, на самом деле его инициализация будет проходить последней. А так как все остальные поля зависят от значения c и инициализируются раньше него, то в них будет содержаться мусор.



Никто нам не может запретить писать список инициализации в том порядке, в котором мы хотим. Да и непонятно, как это сделать. С точки зрения С++, код вполне корректен и может работать. Просто от этого будут последствия, но вполне предсказуемые.



Благо, к нам на помощь приходят компиляторы, которые аккуратно подскажут нам, что мы делаем чухню возможно не то, что хотим. Скорее всего компилятор вам сам кинет ворнинг, что переменная c используется неинициализированной. Чтобы точно заставить компилятор это сделать, добавьте опцию -Wall. Подробнее про ворнинги в этом цикле статей.



Также вы можете(должны?) добавить флаг компиляции, чтобы превращать все предупреждения в ошибки. Тогда вы точно ничего важного не пропустите. Как говорится, флаг -Werror вам в руки!



Можете, кстати, поделиться в комментах историями, как у вас на проектах не стоял этот флаг, рекордным количеством неисправленных ворнингов и последствиями пренебрежением предупредпреждений.



Еще есть нюансы с default member initializer и его сочетанием со списком инициализации конструктора. Короче, много нюансов, как и во всех плюсах)

В один пост все все равно не влезет. Будем разделять и властвовать!



Devide et empera. Stay cool.



#cppcore