Это поведение инициализации члена C ++ хорошо определено? - PullRequest
0 голосов
/ 27 августа 2018

Давайте предположим, что у нас есть класс B с member, который по умолчанию инициализирован как 42.Этот класс знает, как вывести значение его member (он делает это в конструкторе):

struct B
{
  B() : member(42) { printMember(); }

  void printMember() const { std::cout << "value: " << member << std::endl; }

  int member;
};

Затем мы добавляем класс A, который получает константную ссылку на B ипросит B напечатать его значение:

struct A
{
  A(const B& b) { b.printMember(); }
};

Наконец, мы добавляем еще один класс Aggregate, который агрегирует A и B.Сложность в том, что объект a типа A объявлен до объекта b типа B, но затем a инициализируется с помощью (еще не действительной?) Ссылки на b:

struct Aggregate
{
  A a;
  B b;

  Aggregate() : a(b) { }
};

Рассмотрим вывод создания Aggregate (я добавил некоторые записи как для конструктора, так и для деструктора A и B) ( Попробуйте в сети! ):

a c'tor
value: 0
b c'tor
value: 42
b d'tor
a d'tor

Правильно ли я предполагаю, что инициализация a недопустимой ссылкой на (еще не действительный) экземпляр b является неправильной, и, следовательно, это неопределенное поведение?


Мне известен порядок инициализации.Это то, что заставляет меня бороться.Я знаю , что b еще не создан, но я также думаю , чтобы знать, что будущий адрес b может быть определен еще до того, как b будет построен.Поэтому я предположил, что в может быть какое-то правило, о котором я не знаю , которое позволяет компилятору инициализировать b члены по умолчанию до создания b или что-то в этом роде.(Было бы более очевидно, если бы первое распечатанное значение было бы чем-то, что выглядит случайным, а не 0 (значение по умолчанию int)).


Этот ответ помог мне понять, что мне нужно различать

  • , связывающий ссылку с неинициализированным объектом (который действителен) и
  • , обращающийсяпо ссылке неинициализированный объект (который не определен)

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Порядок инициализации членов класса следующий.

Из стандарта CPP (N4713) выделена соответствующая часть:

15.6.2 Инициализация баз и элементов [class.base.init] ...
13 В не делегирующем конструкторе инициализация происходит в следующем порядке:
(13.1) - сначала и только для конструктора самого производного класса (6.6.2) виртуальные базовые классы инициализируются впорядок, в котором они появляются на обходе слева направо по глубине направленного ациклического графа базовых классов, где «слева направо» - это порядок появления базовых классов в списке базовых спецификаторов производного класса.
(13.2) - Затем прямые базовые классы инициализируются в порядке объявления по мере их появления в списке базовых спецификаторов (независимо от порядка mem-initializer).
(13.3) - Затем нестатические члены-данные инициализируются в том порядке, в котором они были объявлены в определении класса (опять же, независимо от порядка mem-инициализаторов).
(13.4) - Наконец, tСоставной оператор тела конструктора выполнен.
[Примечание. Порядок декларации должен гарантировать уничтожение базовых и дочерних подобъектов в обратном порядке инициализации.—Конечная записка]

0 голосов
/ 27 августа 2018

Да, вы правы, что это UB , но по другим причинам, чем просто сохранение ссылки на объект, который не был построен.

Построение учеников происходит в порядке их появления в классе.Хотя адрес B не изменится, и технически вы можете сохранить ссылку на него , как указывал @StoryTeller, вызывая b.printMember() в конструкторе с b, который не былпостроено еще определенно UB.

...