Согласно принятому (и единственному) ответу на этот вопрос переполнения стека ,
Определение конструктора с помощью
MyTest() = default;
вместо этого будет нулевая инициализация объекта.
Тогда почему следующее,
#include <iostream>
struct foo {
foo() = default;
int a;
};
struct bar {
bar();
int b;
};
bar::bar() = default;
int main() {
foo a{};
bar b{};
std::cout << a.a << ' ' << b.b;
}
произвести этот вывод:
0 32766
Оба конструктора определены по умолчанию? Правильно? А для типов POD инициализация по умолчанию - инициализация нуля.
И согласно принятому ответу на этот вопрос ,
Если член POD не инициализируется ни в конструкторе, ни через инициализацию в классе C ++ 11, он инициализируется по умолчанию.
Ответ один и тот же, независимо от стека или кучи.
В C ++ 98 (а не после) новый int () был указан как выполняющий нулевую инициализацию.
Несмотря на попытки обернуть мою (хотя и крошечную ) голову вокруг конструкторов по умолчанию и инициализации по умолчанию , я не смог придумать объяснения.
источник
bar
Конструктор предоставлен пользователем, аfoo
конструктор по умолчанию.a
это ноль.b
не является. Кажетсяa
инициализировано.bar::bar()
видимо вmain()
- оно может быть определено в отдельном модуле компиляции и делать что-то очень нетривиальное, в то время какmain()
только объявление является видимым. Я думаю, вы согласитесь, что это поведение не должно меняться в зависимости от того, помещаете ли выbar::bar()
определение в отдельную единицу компиляции или нет (даже если вся ситуация не интуитивна).int a = 0;
ты хочешь быть откровенным?Ответы:
Проблема здесь довольно тонкая. Вы думаете, что
даст вам конструктор по умолчанию, сгенерированный компилятором, и это так, но теперь он считается предоставленным пользователем. [dcl.fct.def.default] / 5 состояний:
акцент мой
Итак, мы видим, что, поскольку вы не указали значение по умолчанию
bar()
при первом объявлении, оно считается предоставленным пользователем. Из-за этого [dcl.init] /8.2больше не применяется, и мы не инициализируем значение,
b
а вместо этого инициализируем его по умолчанию согласно [dcl.init] /8.1источник
(*_*)
.... Если даже использовать базовые конструкции языка, мне нужно прочитать мелкий шрифт проекта языка, тогда Аллилуйя! Но, вероятно, это то, что вы говорите.bar::bar() = default
вне строки - это то же самое , что и работа в строкеbar::bar(){}
.Различие в поведении исходит из того , что, по мнению
[dcl.fct.def.default]/5
,bar::bar
является пользователем при условии , гдеfoo::foo
нет 1 . Как следствие,foo::foo
будет инициализировать значения своих членов (что означает: инициализация нуляfoo::a
), ноbar::bar
останется неинициализированным 2 .1)
[dcl.fct.def.default]/5
2)
Из ответа Витторио Ромео
источник
Из контекста :
Учитывая это определение,
foo
является агрегатом, аbar
не является (он имеет предоставленный пользователем, не дефолтный конструктор).Поэтому для
foo
,T object {arg1, arg2, ...};
является синтаксис для агрегатной инициализации.Следовательно
a.a
, значение инициализируется, чтоint
означает нулевую инициализацию.Ибо
bar
,T object {};
с другой стороны, это инициализация значения (экземпляра класса, а не инициализация значения членов!). Поскольку это тип класса с конструктором по умолчанию, вызывается конструктор по умолчанию. Конструктор по умолчанию, который вы определили по умолчанию, инициализирует элементы (из-за отсутствия инициализаторов элементов), который в случаеint
(с нестатическим хранилищем) выходитb.b
с неопределенным значением.Нет, это неправильно
PS Несколько слов о вашем эксперименте и вашем заключении: видение того, что результат равен нулю, не обязательно означает, что переменная была инициализирована нулем. Ноль - это вполне возможное число для значения мусора.
Тот факт, что значение было одним и тем же несколько раз, не обязательно означает, что оно также было инициализировано.
Тот факт, что результат одинаков для нескольких опций компилятора, не означает, что переменная инициализирована. (Хотя в некоторых случаях изменение стандартной версии может изменить ее инициализацию).
В C ++ не существует гарантированного способа сделать так, чтобы значение неинициализированного значения выглядело ненулевым.
Единственный способ узнать, что переменная инициализирована, - это сравнить программу с правилами языка и убедиться, что правила говорят, что она инициализирована. В этом случае
a.a
действительно инициализируется.источник
a
инициализация. Я думал,a
что инициализация по умолчанию и инициализация по умолчанию для члена POD, нулевая инициализация. Неa
то просто , к счастью , всегда придумывает нуль, независимо от того , сколько раз я запускаю эту программу.Then how come a is initialized.
потому что это значение инициализировано.I was thinking a is default initialized
Это не.Я попытался запустить предоставленный вами фрагмент
test.cpp
через gcc & clang и несколько уровней оптимизации:Так что вот где он становится интересным, он ясно показывает, что сборка clang O0 читает случайные числа, предположительно, стекового пространства.
Я быстро включил мою IDA, чтобы увидеть, что происходит:
Теперь, что
bar::bar(bar *this)
делает?Хм, ничего. Пришлось прибегнуть к использованию сборки:
Так что да, это просто, ничего, что в основном делает конструктор
this = this
. Но мы знаем, что он на самом деле загружает случайные неинициализированные адреса стека и печатает его.Что если мы явно предоставим значения для двух структур?
Хит лязг, упс
Аналогичная судьба и с g ++:
Таким образом, это означает, что это фактически прямая инициализация
bar b(0)
, а не совокупная инициализация.Вероятно, это связано с тем, что если вы не предоставите явную реализацию конструктора, это может быть внешний символ, например:
Компилятор не достаточно умен, чтобы выводить это как неактивный / встроенный вызов на неоптимизированной стадии.
источник