2012-05-23 8 views
22

Рассмотрим следующий (упрощенный) ситуации:Могут ли переменные-члены использоваться для инициализации других членов в списке инициализации?

class Foo 
{ 
private: 
    int evenA; 
    int evenB; 
    int evenSum; 
public: 
    Foo(int a, int b) : evenA(a-(a%2)), evenB(b-(b%2)), evenSum(evenA+evenB) 
    { 
    } 
}; 

Когда я Foo, как создании экземпляра этого:

Foo foo(1,3); 

затем evenA равно 0, evenB равно 2, но будет evenSum быть инициализирована до 2?

Я пробовал это на своей нынешней платформе (iOS) и, похоже, работает, но я не уверен, что этот код переносимый.

Благодарим за помощь!

+0

Это один из опасных углов в C++. – iammilind

+0

Codepad - отличное место, чтобы проверить такие вещи: http://codepad.org/uFgZpkwN –

+0

@Agent_L: Это не скажет вам, является ли этот код переносимым. –

ответ

27

Этот является четко определенным и портативным, , но он потенциально подвержен ошибкам.

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


1. Из [class.base.init] в стандарте С ++ (ы):

В не делегировании конструктор, инициализация происходит в следующем порядке:

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

(Выделение мое.)

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

+0

Итак, если Foo имеет байт базового класса (не виртуальное публичное наследование), и я помещаю конструктор Bars в список инициализации, он всегда будет выполняться перед всеми инициализаторами членов, даже если я поместил его в конец списка инициализации? – Pontomedon

+0

@ Понтомедон: Да. Конструкторы базового класса всегда вызывают сначала (даже если они вообще не входят в список). –

6

Члены инициализируются в том порядке, в котором они объявлены в определении класса. Пока ваш список инициализаторов следует этому порядку, он должен быть в порядке.

0

Это также скомпилировано без ошибок в g ++ 4.0.3 (сейчас 6 лет).

Я уверен, что это скомпрометирует любой разумно недавний компилятор.

8

Да, представьте, что они уже построены. Только не забудьте , что порядок построения - это порядок деклараций в определении класса , не порядок инициализаторов в конструкторе . И что компилятор обычно не скажет вам, используете ли вы переменную до ее создания. В вашем случае, например, при перемещении evenSum в верхнюю части класса, вы неопределенные поведения (потому что его инициализатор использует неинициализированные член), даже хотя в конструкторе, вы инициализировать evenA и evenB лексически перед evenSum.