Подтвердить что ты не робот

Является ли он корректным/законным для размещения - несколько раз по одному и тому же адресу?

(Примечание: этот вопрос был мотивирован, пытаясь придумать хакерство препроцессора, чтобы генерировать распределение без операции для ответа на этот другой вопрос:

Макрос, который принимает новый объект

... так имейте это в виду!)

Здесь надуманный класс:

class foo {
private:
    int bar;
public:
    foo(int bar) : bar (bar)
        { std::cout << "construct foo #" << bar << std::endl; }
    ~foo()
        { std::cout << "destruct foo #" << bar << std::endl; }
};

... который я буду выделять следующим образом:

// Note: for alignment, don't use char* buffer with new char[sizeof(foo)] !
void* buffer = operator new(sizeof(foo));

foo* p1 = new (buffer) foo(1);
foo* p2 = new (buffer) foo(2);

/* p1->~foo(); */ /* not necessary per spec and problematic in gen. case */
p2->~foo();

В gcc у меня есть "ожидаемый" результат:

construct foo #1
construct foo #2
destruct foo #2

Это здорово, но может ли компилятор/среда выполнения отклонить это как злоупотребление и все еще быть в правой части спецификации?

Как насчет потоковой передачи? Если на самом деле мы не заботимся о содержимом этого класса (пусть это все-таки фиктивный объект), то он, по крайней мере, не потерпит крах, например, в еще более простом приложении, которое мотивировало это с помощью POD int?

4b9b3361

Ответ 1

Формирование размещения - несколько раз в одном блоке памяти - отлично. Более того, как ни странно это звучит, вы даже не обязаны уничтожать объект, который уже находится в этой памяти (если есть). Стандарт явно позволяет, что в 3.8/4

4. Программа может завершить время жизни любого объекта за счет повторного использования хранилища который объект занимает или явно называя деструктор для объект типа класса с нетривиальным деструктором. Для объекта типа класса с нетривиальным деструктором, программа не требуется для вызова деструктора явно перед хранилищем, которое объект занимает повторно или освобождается, [...]

Другими словами, вы несете ответственность за последствия отказа от вызова деструктора для какого-либо объекта.

Однако вызов деструктора на том же объекте, что и в вашем коде, не разрешен. Как только вы создали второй объект в той же области памяти, вы фактически закончили жизнь первого объекта (хотя вы никогда не называли его деструктором). Теперь вам нужно всего лишь уничтожить второй объект.

Ответ 2

foo* p1 = new (buffer) foo(1);
foo* p2 = new (buffer) foo(2);
p1->~foo();
p2->~foo();

Вы разрушаете один и тот же объект дважды, и это только поведение undefined. Ваша реализация может решить заказать пиццу, когда вы это сделаете, и она по-прежнему будет с правой стороны спецификации.

Тогда есть тот факт, что ваш буфер не может быть правильно выровнен для emplace объекта типа foo, который снова не является стандартным С++ (согласно С++ 03, я думаю, что С++ 11 расслабляет это).

Update: Что касается вопроса, указанного в названии,

Является ли он корректным/законным для размещения - несколько раз по одному и тому же адресу?

Да, он хорошо определен для размещения - несколько раз с одним адресом при условии, что он указывает на необработанную память.

Ответ 3

Нет - это выглядит неправильно.

Когда вы используете размещение new, объект будет построен на адресу, который вы передадите. В этом примере вы дважды передаете один и тот же адрес (например, & buffer [0]), поэтому второй объект просто стирает первый объект, который уже был сконструирован в этом месте.

EDIT: Я не думаю, что понимаю, что вы пытаетесь сделать.

Если у вас есть общий тип объекта (который может иметь нетривиальный ctor/dtor, который может выделять/освобождать ресурсы), и вы уничтожаете первый объект путем размещения new 'поверх его, не вызывая его прямое обращение деструктор, это, по крайней мере, будет утечкой памяти.