Не определено ли инициализировать члена класса в перегруженном операторе new? - PullRequest
3 голосов
/ 30 июня 2011

Возьмите небольшой пример, где я пытаюсь выяснить, расположена ли переменная в куче или нет:

struct A
{
  bool isOnHeap;
  A () {}  // not touching isOnHeap
 ~A () {}

  void* operator new (size_t size)
  {
    A* p = (A*) malloc(size);
    p->isOnHeap = true;  // setting it to true
    return p;
  }
  void operator delete (void *p) { free(p); }
};

Это дает ожидаемый результат в g ​​++ - 4,5 (с предупреждением для стекового объекта). Это плохо определено делать такие операции?

Ответы [ 3 ]

6 голосов
/ 30 июня 2011

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

У вас нет гарантии, что реализация не сотрет память между временем, возвращаемым operator new, и временем начала строительства объекта, или тем, что во время создания объекта члены имеют неопределенное значение в стандарте (например, потому что они являются POD и явно не инициализируются в конструкторе, таком как isOnHeap), намеренно не устанавливаются в что-либо реализацией.

Обратите внимание, что A имеет нетривиальный конструктор (он объявлен пользователем), поэтому его время жизни не начинается, когда выделяется хранилище для объекта (ISO / IEC 14882: 2003, 3.8 [basic.life ] / 1) и программа имеет неопределенное поведение , если она использует указатель на хранилище для объекта для доступа к нестатическому элементу данных (3.8 / 5). Даже если A был POD-типом, его значение после завершения new-expression все равно будет неопределенным, а не обязательно связанным со значениями в байтах в хранилище для объекта до новое выражение было оценено.

3 голосов
/ 30 июня 2011

Как сказал Чарльз, объект оживает только после того, как он был обновлен, поэтому установка данных в вашей реализации new довольно опасна.

Кроме того, когда ваши разработчики используют такие инструменты, как Lint, возникает большая проблема.вероятность того, что он жалуется на то, что член isOnHeap не инициализирован в конструкторе.Если кто-то подумает: «Эй, Линт прав, давайте инициализируем isOnHeap в конструкторе A», это подорвет механизм, которого вы пытаетесь достичь.

Есть второй случай, который вы, вероятно, не сделалисчитать.Предположим, что кто-то пишет это:

class MyClass
   {
   public:
      ...
   private:
      struct A m_a;
   };

int main()
   {
   MyClass *myVariable = new MyClass();
   }

Тогда ваша реализация new не будет вызвана.Тем не менее, экземпляр A размещается в куче (как часть экземпляра MyClass).

Можете ли вы объяснить, почему вы хотите знать, было ли что-то выделено в куче или нет?Может быть, есть другое, более элегантное решение вашей проблемы.

3 голосов
/ 30 июня 2011

Даже если не учитывать сам оператор new (который нестандартен, и я бы даже сказал, некрасиво, но зная точные детали какого-то конкретного компилятора, он может быть работоспособен), есть еще одна проблема, которая в любом случае делает его бесполезным: У вас нет гарантии, что значение od isOnHeap не будет истинным при размещении в стеке. Стек не инициализирован, и там можно найти любой мусор из вызовов функций, выполненных до этого.

...