Является ли это четко определенным / законным для размещения новых несколько раз по одному и тому же адресу? - PullRequest
13 голосов
/ 23 сентября 2011

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

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

... помните об этом!)

Вот надуманный класс:

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?

Ответы [ 3 ]

15 голосов
/ 23 сентября 2011

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

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

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

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

3 голосов
/ 23 сентября 2011
foo* p1 = new (buffer) foo(1);
foo* p2 = new (buffer) foo(2);
p1->~foo();
p2->~foo();

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

Кроме того, существует тот факт, что ваш буфер может быть неправильно выровнен для размещения объекта типа foo, что опять-таки является нестандартным C ++ (в соответствии с C ++ 03, я думаю, C ++ 11 ослабляет это). *

Обновление: По вопросу, указанному в заголовке,

Хорошо ли определено / законно многократное размещение нового на одном и том же адресе?

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

0 голосов
/ 23 сентября 2011

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

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

РЕДАКТИРОВАТЬ: Я не думаю, что я понимаю, что вы пытаетесь сделать.

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

...