Законность использования оператора delete на указателе, полученном при размещении new - PullRequest
12 голосов
/ 11 декабря 2010

Я уверен, что этот код должен быть недопустимым, поскольку он явно не будет работать, но, похоже, он разрешен для C ++ 0x FCD.

class X { /* ... */};
void* raw = malloc(sizeof (X));
X* p = new (raw) X(); // according to the standard, the RHS is a placement-new expression
::operator delete(p); // definitely wrong, per litb's answer
delete p; // legal?  I hope not

Возможно, один изВы, юристы, изучающие язык, можете объяснить, как это запрещает стандарт.

Существует также форма массива:

class X { /* ... */};
void* raw = malloc(sizeof (X));
X* p = new (raw) X[1]; // according to the standard, the RHS is a placement-new expression
::operator delete[](p); // definitely wrong, per litb's answer
delete [] p; // legal?  I hope not

Это ближайший вопрос Мне удалось найти.

РЕДАКТИРОВАТЬ: Я просто не покупаю аргумент, что язык стандарта, ограничивающий аргументы для функции void ::operator delete(void*), применим любым значимым образом к операнду delete в delete-expression .В лучшем случае соединение между ними является чрезвычайно ненадежным, и ряд выражений допускается в качестве операндов для delete, которые недопустимы для передачи в void ::operator delete(void*).Например:

struct A
{
  virtual ~A() {}
};

struct B1 : virtual A {};

struct B2 : virtual A {};

struct B3 : virtual A {};

struct D : virtual B1, virtual B2, virtual B3 {};

struct E : virtual B3, virtual D {};

int main( void )
{
  B3* p = new E();
  void* raw = malloc(sizeof (D));
  B3* p2 = new (raw) D();

  ::operator delete(p); // definitely UB
  delete p; // definitely legal

  ::operator delete(p2); // definitely UB
  delete p2; // ???

  return 0;
}

Я надеюсь, что это показывает, что то, может ли указатель быть передан в void operator delete(void*), не имеет никакого отношения к тому, может ли этот же указатель использоваться в качестве операнда delete.

Ответы [ 4 ]

7 голосов
/ 11 декабря 2010

Стандартные правила в [basic.stc.dynamic.deallocation] p3

В противном случае значение, предоставленное operator delete(void*) в стандартной библиотеке, должно быть одним из значений, возвращаемых предыдущим вызовомлибо operator new(size_t) или operator new(size_t, const std::nothrow_t&) в стандартной библиотеке, а значение, предоставленное operator delete[](void*) в стандартной библиотеке, должно быть одним из значений, возвращаемых предыдущим вызовом либо operator new[](size_t), либо operator new[](size_t, const std::nothrow_t&) в стандартной библиотеке.

Ваш delete вызов вызовет библиотеки operator delete(void*), если вы не перезаписали его.Поскольку вы ничего не сказали об этом, я предполагаю, что вы этого не сделали.

Выше «на самом деле» должно быть что-то вроде «поведение не определено, если нет», поэтому оно не ошибочно считается диагностируемым правилом, которого не придерживается [lib.res.on.arguments] p1.Это было исправлено n3225, поэтому больше не может ошибаться.

3 голосов
/ 11 декабря 2010

Компилятору на самом деле все равно, что p исходит от вызова размещения new, поэтому он не помешает вам выдать delete на объект.Таким образом, ваш пример можно считать «легальным».

Это не сработает, поскольку операторы «размещения delete» не могут быть вызваны явно.Они вызываются неявно только в том случае, если конструктор генерирует ошибку, поэтому деструктор может работать.

2 голосов
/ 11 декабря 2010

Я нашел это в разделе библиотеки стандарта, который является настолько нелогичным местоположением (IMO), насколько это возможно:

C ++ 0x FCD (и черновик n3225) раздел 18.6.1.3, [new.delete.placement]:

Эти функции зарезервированы, C ++ Программа может не определять функции, которые сместить версии в Стандарте Библиотека C ++ (17.6.3). Положения из (3.7.4) не относятся к этим Зарезервированные формы размещения оператора новый и оператор удалить.

void*  operator  new(std::size_t  size,  void*  ptr)  throw();
void*  operator  new[](std::size_t  size,  void*  ptr)  throw();
void  operator  delete(void*  ptr,  void*)  throw();
void  operator  delete[](void*  ptr,  void*)  throw();

Тем не менее, раздел, определяющий юридические выражения для передачи delete, равен 5.3.5, а не 3.7.4.

2 голосов
/ 11 декабря 2010

Полагаю, вам это может сойти с рук (на конкретном компиляторе), если

  1. new / delete реализованы в терминах malloc / free и
  2. размещение new фактически использует тот же механизм для отслеживания деструктора, связанного с выделениями, что и стандарт new, чтобы при вызове delete можно было найти правильный деструктор.

Но нет никакого способа, которым это может быть портативным или безопасным.

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