Дополнительная конструкция при использовании размещения нового с классом хранения - PullRequest
1 голос
/ 21 января 2020

В ситуации, когда я хочу избежать динамического выделения памяти c, я заменяю новый оператор процессом, который по существу использует память некоторого статически распределенного объекта (класс Storage ниже). Ниже приведен минимальный рабочий пример:

#include <cassert>
#include <iostream>

struct Object { 
  Object() { std::cout << "Creating a new object\n"; } 
  static void *operator new(size_t);
  static void operator delete(void *p);
};

static struct { 
  Object where;
  bool allocated = false;
} Storage; // 1

void *Object::operator new(size_t) { 
  assert(!Storage.allocated);
  auto p = ::new (&Storage.where) Object; // 2
  Storage.allocated = true;

  return p;
}

void Object::operator delete(void *p) { 
  assert(Storage.allocated);
  static_cast<Object *>(p)->~Object();
  Storage.allocated = false;
}

int main() { Object *obj = new Object; } // 3

Мой вопрос касается количества обращений к конструктору . Когда я запускаю вышеуказанную программу, я ожидаю дважды вызвать конструктор (помеченный как 1 и 2 в комментариях выше), но получаю вывод:

Создание новой объект

Создание нового объекта

Создание нового объекта

Почему конструктор называется трижды? Я ожидал бы только вызовов конструктора от объекта stati c и вызова для размещения new. Я попытался отследить код с помощью gdb, но для меня это не имеет смысла, так как позиция //3 равна where, когда происходит третий вызов конструктора.

Причина, которую я хочу знать, заключается в том, что возник случай, когда этот дополнительный вызов конструктора вызывает нежелательные побочные эффекты; до сих пор этот дополнительный звонок был незамеченным.

Ответы [ 2 ]

6 голосов
/ 21 января 2020

По какой-то странной причине ваш operator new вызывает конструктор, когда ему просто нужно выделить память. Это означает, что вызов new завершается вызовом конструктора Object дважды. Один вызов в operator new и другой вызов в main.

Возможно, вы захотите следующее:

void *Object::operator new(size_t) { 
  assert(!Storage.allocated);
  Storage.allocated = true;
  return reinterpret_cast<void *> (&Storage.where);
}

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

Object *obj = new Object(7);

Как operator new узнает, как правильно построить объект? Это не то место, где ты должен это делать!

4 голосов
/ 21 января 2020

Object *obj = new Object; делает две вещи:

  1. Выделяет память, вызывая operator new

  2. Вызывает конструктор.

Ваш operator new также вызывает конструктор, поэтому этот оператор вызывается дважды (и один раз для инициализации глобальной переменной).

Обратите внимание, что delete - это то же самое , delete obj; делает две вещи:

  1. Вызывает деструктор.

  2. Освобождает память, вызывая operator delete

Ваш operator delete также не должен вызывать деструктор, потому что тогда деструктор вызывается дважды.

...