Будет ли список инициализации всегда обрабатываться перед кодом конструктора? - PullRequest
15 голосов
/ 07 апреля 2009

Будет ли всегда обрабатываться список инициализации перед кодом конструктора?

Другими словами, будет ли следующий код всегда печатать <unknown>, и созданный класс будет иметь «известное» значение как значение для source_ (если глобальная переменная something равна true)?

class Foo {
  std::string source_;
public:
  Foo() : source_("<unknown>") {
    std::cout << source_ << std::endl;
    if(something){
      source_ = "known";
    }
  }
};

Ответы [ 3 ]

20 голосов
/ 07 апреля 2009

Да, в соответствии с C++11: 12.6.2 /10 (тот же раздел в C++14, 15.6.2 /13 в C++17):


В конструкторе без делегирования инициализация происходит в следующем порядке (выделено жирным шрифтом):

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

  • Затем прямые базовые классы инициализируются в порядке объявления по мере их появления в списке базовых спецификаторов (независимо от порядка mem-initializer).

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

  • Наконец, составной оператор тела конструктора выполняется.


Основная причина использования init-lists - помочь компилятору в оптимизации. Списки инициализации для неосновных типов (т. Е. Объектов класса, а не int, float и т. Д.), Как правило, могут создаваться на месте.

Если вы создаете объект, а затем назначаете его в конструкторе, это обычно приводит к созданию и уничтожению временных объектов, что неэффективно.

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

Следующая полная программа выведет 7, но это для определенного компилятора (CygWin g ++), поэтому он не гарантирует такого поведения, как пример из исходного вопроса.

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

#include <iostream>
class Foo {
    int x;
    public:
        Foo(): x(7) {
            std::cout << x << std::endl;
        }
};
int main (void) {
    Foo foo;
    return 0;
}
7 голосов
/ 07 апреля 2009

Как уже ответили, списки инициализации полностью выполняются до входа в блок конструктора. Таким образом, полностью безопасно использовать (инициализированные) члены в теле конструктора.

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

Возможно, вы ошиблись тем, что должен ссылаться на параметры, а не на атрибуты внутри списка инициализации. В качестве примера, для класса X, который имеет два члена (a_ и b_) типа int, следующий конструктор может быть неправильно определен:

 X::X( int a ) : a_( a ), b( a_*2 ) {}

Возможная проблема здесь заключается в том, что построение элементов в списке инициализации зависит от порядка объявления в классе, а не от порядка, в котором вы вводите список инициализации. Если класс был определен как:

class X
{
public:
   X( int a );
private:
   int b_;
   int a_; 
};

Тогда, независимо от того, как вы вводите список инициализации, факт заключается в том, что b_ (a_ * 2) будет выполнено до инициализации a_, так как объявление членов сначала b_, а затем a_ , Это создаст ошибку, поскольку ваш код считает (и, вероятно, зависит), что b_ в два раза больше значения a_, и фактически b_ содержит мусор. Самое простое решение не ссылаться на членов:

 X::X( int a ) : a_( a ), b( a*2 ) {} // correct regardless of how X is declared

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

7 голосов
/ 07 апреля 2009

Да, C ++ создает все члены перед вызовом кода конструктора.

...