Что означают следующие фразы в C ++: инициализация нуля, значения по умолчанию и значения?

190

Что означают следующие фразы в C ++:

  • нуль-инициализации,

  • инициализация по умолчанию, и

  • Значение инициализация

Что должен знать о них разработчик C ++?

Билл
источник
1
Это связано с (но не идентично) stackoverflow.com/questions/620137/…
Стив Джессоп
20
Есть еще кое-что! Полный список инициализаций: Value, direct, copy, list (C ++ 11 new intro), агрегат, ссылка, ноль, константа и значение по умолчанию; en.cppreference.com/w/cpp/language/initialization перечисляет их всех с примерами :)
legends2k

Ответы:

65

Одна вещь, которую нужно осознать, это то, что «инициализация значения» является новой в стандарте C ++ 2003 - она ​​не существует в исходном стандарте 1998 года (я думаю, что это может быть единственным отличием, которое больше, чем пояснение). См . Ответ Кирилла В. Лядвинского на определения прямо из стандарта.

Посмотрите этот предыдущий ответ о поведении operator newдля деталей относительно различного поведения этих типов инициализации и когда они вступают в силу (и когда они отличаются от c ++ 98 до C ++ 03):

Суть ответа такова:

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

  • В C ++ 1998 есть 2 типа инициализации: ноль и по умолчанию
  • В C ++ 2003 был добавлен третий тип инициализации, инициализация значения.

По меньшей мере, это довольно сложно, и когда различные методы применяются тонко.

Обязательно нужно знать, что MSVC следует правилам C ++ 98 даже в VS 2008 (VC 9 или cl.exe версии 15.x).

Следующий фрагмент показывает, что MSVC и Digital Mars следуют правилам C ++ 98, а GCC 3.4.5 и Comeau - правилам C ++ 03:

#include <cstdio>
#include <cstring>
#include <new>

struct A { int m; }; // POD
struct B { ~B(); int m; }; // non-POD, compiler generated default ctor
struct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m

int main()
{
    char buf[sizeof(B)];
    std::memset( buf, 0x5a, sizeof( buf));

    // use placement new on the memset'ed buffer to make sure 
    //  if we see a zero result it's due to an explicit 
    //  value initialization
    B* pB = new(buf) B();   //C++98 rules - pB->m is uninitialized
                            //C++03 rules - pB->m is set to 0
    std::printf( "m  is %d\n", pB->m);
    return 0;
}
Майкл Берр
источник
1
Не то чтобы это важно int, но m()в третьей строке значение инициализирует m. Важно, если вы измените int m;на B m;. :)
Йоханнес Шауб -
Правильно - Aи Cне используются в этом примере (они перенесены из другого связанного ответа). Даже при том, что C ++ 98 и C ++ 03 используют различную терминологию при описании того, как Aи как Cони сконструированы, результат одинаков в обоих стандартах. Только struct Bприводит к другому поведению.
Майкл Берр
1
Я имел в виду, что если вы измените C на struct C { C() : m() {}; ~C(); B m; };, то у вас будет m.m0. Но если он будет инициализирован по умолчанию, mкак вы говорите в C ++ 03, то m.mон не будет инициализирован, как в C ++ 98.
Йоханнес Шауб -
1
Дополнительные интересные комментарии по обработке этой функции в MSVC: stackoverflow.com/questions/3931312/…
Брент Брэдберн
Какая инициализация происходит, когда вы объявляете свой тип как локальную переменную, то есть в стеке?
Андре Пуэль
89

C ++ 03 Стандарт 8.5 / 5:

Для того, чтобы нуль-инициализации объекта типа Т означает:
- если Т представляет собой тип скаляр (3.9), то объект устанавливается в значение 0 (ноль) преобразуется в T;
- если T является типом класса, не являющимся объединением, каждый нестатический член данных и каждый подобъект базового класса инициализируются нулями;
- если T является типом объединения, первый именованный элемент данных объекта инициализируется нулями;
- если T является типом массива, каждый элемент инициализируется нулями;
- если T является ссылочным типом, инициализация не выполняется.

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

Для того, чтобы значение инициализации объекта типа Т означает:
- если Т типа класса (пункт 9) с пользователем объявлено конструктором (12.1), то конструктор по умолчанию для Т называется (и инициализация плохо формируется , если Т не имеет доступного конструктора по умолчанию);
- если T является типом класса без объединения без конструктора, объявленного пользователем, то каждый нестатический член данных и компонент базового класса в T инициализируются значением;
- если T является типом массива, то каждый элемент инициализируется значением;
- иначе объект инициализируется нулями

Программа, которая вызывает инициализацию по умолчанию или инициализацию значения сущности ссылочного типа, плохо сформирована. Если T является cv-квалифицированным типом, cv-неквалифицированная версия T используется для этих определений инициализации нуля, инициализации по умолчанию и инициализации значения.

Кирилл В. Лядвинский
источник
18
Это может быть устаревшим для C ++ 11. cppreference.com заявляет, что инициализация по умолчанию не инициализирует элементы с нуля (только инициализация значения).
Алексей Шолик
3
@android поднимает важный вопрос, на который я не вижу ответа в другом месте, поэтому я задал новый вопрос. stackoverflow.com/questions/22233148/…
Адриан МакКарти