Гарантирует ли стандарт C ++, что неинициализированные члены POD сохраняют свое прежнее значение после размещения нового?
Или, точнее, будет ли всегда выполняться следующее утверждение согласно C ++ 11?
#include <cstdlib>
#include <cassert>
struct Foo {
int alpha; // NOTE: Uninitialized
int beta = 0;
};
int main()
{
void* p = std::malloc(sizeof (Foo));
int i = some_random_integer();
static_cast<Foo*>(p)->alpha = i;
new (p) Foo;
assert(static_cast<Foo*>(p)->alpha == i);
}
Одинаков ли ответ для C ++ 03?
Гарантирует ли стандарт C ++, что неинициализированные члены POD сохраняют свое прежнее значение после размещения нового?
Будет ли всегда выполняться следующее утверждение в соответствии с C ++ 11?
Нет.
Неинициализированные члены данных имеют неопределенный значение, и это совсем не то же самое, что сказать, что базовая память остается одна.
[C++11: 5.3.4/15]:
новое выражение который создает объект типаT
инициализирует этот объект следующим образом:
- Если новый инициализатор опущен, объект по умолчанию инициализируется (8.5); если инициализация не выполняется, объект имеет неопределенное значение.
- В противном случае новый инициализатор интерпретируется в соответствии с правилами инициализации 8.5 для прямой инициализации.
[C++11: 8.5/6]:
По умолчанию инициализировать объект типаT
средства:
- если
T
это (возможно, резюме квалифицированных) тип класса (Пункт 9), конструктор по умолчанию дляT
называется (и инициализация плохо сформирована, еслиT
не имеет доступного конструктора по умолчанию);- если
T
тип массива, каждый элемент инициализируется по умолчанию;- в противном случае инициализация не выполняется.
[C++11: 12.1/6]:
Конструктор по умолчанию, который по умолчанию и не определен как удаленный, неявно определяется, когда он УСО используемый (3.2) создать объект своего типа (1.8) или когда он явно установлен по умолчанию после первого объявления. Неявно определенный конструктор по умолчанию выполняет набор инициализаций класса, который
выполняется пользовательским конструктором по умолчанию для этого класса без т е р-инициализатор (12.6.2) и пустой компаунд-заявление.
[C++11: 12.6.2/8]:
В не делегирующем конструкторе если данный нестатический член данных или базовый класс не обозначен мем-инициализатор-идентификатор (включая случай, когда нет мем-инициализатора-лист потому что конструктор не имеет т е р-инициализатор) и сущность не является виртуальным базовым классом абстрактного класса (10.4), то
- если объект является нестатическим членом данных, который имеет скобки или равно-инициализатор, объект инициализируется, как указано в 8.5;
- в противном случае, если объект является вариантом члена (9.5), инициализация не выполняется;
- в противном случае объект инициализируется по умолчанию (8.5).
(NB. первый вариант в 12.6.2/8
как ваш член beta
обрабатывается)
[C++11: 8.5/6]:
По умолчанию инициализировать объект типаT
средства:
- если
T
это (возможно резюме квалифицированных) тип класса (раздел 9), конструктор по умолчанию дляT
называется (и инициализация плохо сформирована, еслиT
не имеет доступного конструктора по умолчанию);- если
T
тип массива, каждый элемент инициализируется по умолчанию;- в противном случае инициализация не выполняется.
[C++11: 8.5/11]:
Если для объекта не указан инициализатор, объект инициализируется по умолчанию; если инициализация не выполняется, объект с автоматическим или динамическим сроком хранения имеет неопределенное значение.
Компилятор может выбрать обнуление (или иное изменение) основной памяти во время выделения. Например, известно, что Visual Studio в режиме отладки записывает распознаваемые значения, такие как 0xDEADBEEF
в память, чтобы помочь отладке; в этом случае вы, вероятно, увидите 0xCDCDCDCD
который они используют для обозначения «чистой памяти» (ссылка).
Будет это в этом случае? Я не знаю. Я не думаю, что мы Можно знать.
Что мы делать Я знаю, что C ++ не запрещает это, и я считаю, что это приводит нас к выводу об этом ответе. 🙂
Одинаков ли ответ для C ++ 03?
да, хотя по несколько иной логике:
[C++03: 5.3.4/15]:
новое выражение который создает объект типаT
инициализирует этот объект следующим образом:
- Если новый инициализатор опущен:
- Если
T
это (возможно, резюме квалифицированных) класс не POD (или его массив), объект инициализируется по умолчанию (8.5). ЕслиT
является const-квалифицированным типом, базовый тип класса должен иметь объявленный пользователем конструктор по умолчанию.- В противном случае созданный объект имеет неопределенное значение. Если
T
является константным типом, или (возможно резюме квалифицированных) Тип класса POD (или его массив), содержащий (прямо или косвенно) член типа с константой, программа некорректна;- Если новый инициализатор имеет форму
()
элемент инициализируется значением (8.5);- Если новый инициализатор имеет форму
(expression-list)
а такжеT
является типом класса, соответствующий конструктор вызывается, используяexpression-list
в качестве аргументов (8.5);- Если новый инициализатор имеет форму
(expression-list)
а такжеT
является арифметикой, перечислением, указателем или указателем на член типа иexpression-list
содержит ровно одно выражение, затем объект инициализируется (возможно, преобразованным) значением выражения (8.5);- В противном случае новое выражение плохо сформирован.
Теперь все это было моим строгий интерпретация правил инициализации.
Говоря практически, я думаю, что вы, вероятно, правы, видя потенциальный конфликт с определением размещения operator new
синтаксис:
[C++11: 18.6.1/3]:
Примечания: Преднамеренно не выполняет никаких других действий.
В следующем примере объясняется, что размещение new
msgstr «может быть полезно для построения объекта по известному адресу».
Тем не менее, это на самом деле не говорит об общем использовании построения объекта по известному адресу не балуя ценности, которые уже были там, но фраза «не выполняет никаких других действий» действительно предполагает, что ваша «неопределенная ценность» будет такой, какой была в памяти ранее.
Кроме того, он может просто запретить оператор Сам не предпринимать никаких действий, оставляя распределитель свободным для. Это делает Мне кажется, что важный момент, который пытается сделать стандарт, заключается в том, что новая память не выделяется.
Независимо от того, доступ к этим данным вызывает неопределенное поведение:
[C++11: 4.1/1]:
Glvalue (3.10) нефункционального типа, не являющегося массивомT
может быть преобразован в prvalue. ЕслиT
является неполным типом, программа, которая требует этого преобразования, плохо сформирована. Если объект, на который ссылается glvalue, не является
объект типаT
и не является объектом типа, полученного изT
, или же если объект неинициализирован, программа, которая требует этого преобразования, имеет неопределенное поведение. ЕслиT
это тип, не относящийся к классу, тип prvalue — версия cv-unqualifiedT
, В противном случае тип prvalueT
,
Так это не имеет значения: вы все равно не могли бы соблюдать оригинальное значение.
C ++ 11 12.6.2 / 8 «Инициализация баз и членов» гласит:
В не делегирующем конструкторе, если данный нестатический элемент данных или
базовый класс не обозначается идентификатором mem-initializer-id (включая
случай, когда нет mem-initializer-list, потому что конструктор
не имеет инициализатора ctor), и объект не является виртуальным базовым классом
абстрактный класс (10.4), тогда
- если объект является нестатическим элементом данных, который имеет инициализатор с фигурной или равной скобкой, объект инициализируется, как указано в
8,5;- в противном случае, если объект является вариантом члена (9.5), инициализация не выполняется;
- в противном случае объект инициализируется по умолчанию (8.5).
Инициализация по умолчанию на int
ничего не делает (8.5 / 6 «Инициализаторы»):
По умолчанию инициализировать объект типа T означает:
- если T является (возможно, cv-квалифицированным) типом класса (раздел 9), вызывается конструктор по умолчанию для T (и инициализация
плохо сформирован, если T не имеет доступного конструктора по умолчанию);- если T является типом массива, каждый элемент инициализируется по умолчанию;
- в противном случае инициализация не выполняется.
Итак, член alpha
должен быть оставлен в покое.