Доступ к объектам с помощью reinterpret_cast
ed указателей и связанных с ними UB подробно обсуждался здесь. После прочтения вопросов и ответов я все еще не уверен в правильном использовании неинициализированной памяти с типами POD.
Предположим, я хочу "подражать"
struct { double d; int i; };
путем ручного выделения памяти для элементов данных и предположения (для простоты), что до i
.
не требуется заполнение.
Теперь я делаю это:
// (V1)
auto buff = reinterpret_cast<char*>(std::malloc(sizeof(double) + sizeof(int)));
auto d_ptr = reinterpret_cast<double*>(buff);
auto i_ptr = reinterpret_cast<int*>(buff + sizeof(double));
*d_ptr = 20.19;
*i_ptr = 2019;
Первый вопрос: действителен ли этот код?
Я мог бы использовать размещение new
:
// (V2)
auto buff = reinterpret_cast<char*>(std::malloc(sizeof(double) + sizeof(int)));
auto d_ptr = new(buff) double;
auto i_ptr = new(buff + sizeof(double)) int;
*d_ptr = 20.19;
*i_ptr = 2019;
Должен ли я? Размещение new
представляется здесь избыточным, потому что инициализация по умолчанию для типов POD по умолчанию не используется (пустая инициализация), а [basic.life] читает:
Время жизни объекта типа T
начинается, когда:
(1.1) получается хранилище с правильным выравниванием и размером для типа T
,
(1.2) если объект имеет не пустую инициализацию, его инициализация завершена, ...
Значит ли это, что время жизни объектов *d_ptr
и *i_ptr
началось после того, как я выделил для них память?
Второй вопрос: могу ли я использовать double*
(или немного T*
) для buff
, т.е.
// (V3)
auto buff = reinterpret_cast<double*>(std::malloc(sizeof(double) + sizeof(int)));
auto d_ptr = reinterpret_cast<double*>(buff);
auto i_ptr = reinterpret_cast<int*>(buff + 1);
*d_ptr = 20.19;
*i_ptr = 2019;
или
// (V4)
auto buff = reinterpret_cast<double*>(std::malloc(sizeof(double) + sizeof(int)));
auto d_ptr = new(buff) double;
auto i_ptr = new(buff + 1) int;
*d_ptr = 20.19;
*i_ptr = 2019;