是否未定义在重载的operator new中初始化类成员?

时间:2011-06-30 06:19:04

标签: c++ initialization new-operator undefined-behavior

举一个小例子,我试图找出是否在堆上分配了一个变量:

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); }
};

它给出expected result in g++-4.5(对堆栈对象有警告)。这是不明确的定义 做这样的手术?

3 个答案:

答案 0 :(得分:6)

您无法在重载的operator new中初始化类成员,因为该对象的生命周期尚未开始。您只能在构造对象期间初始化成员。

您无法保证实现不会在时间operator new返回和对象构造开始的时间之间擦除内存,也不保证在指定具有标准的不确定值的对象构造成员期间擦除内存(例如,因为它们是POD并且没有像isOnHeap那样在构造函数中显式初始化,所以不会被实现故意设置为某种东西。

请注意A有一个非平凡的构造函数(它是用户声明的),因此在分配对象的存储时它的生命周期不会启动(ISO / IEC 14882:2003,3.8 [基本.life] / 1)如果程序使用指向存储的指针来访问非静态数据成员(3.8 / 5),则该程序具有未定义行为。即使A是POD类型,它在 new-expression 完成后的值仍然是不确定的,而不一定与对象的存储中的字节值相关在评估 new-expression 之前。

答案 1 :(得分:3)

即使不考虑运算符new本身(这是非标准的,我甚至会说丑陋,但知道某些特定编译器的确切细节可能是可行的),还有另一个问题,无论如何都会使它无用:您无法保证在堆栈上分配时,值isOnHeap不会为真。堆栈没有初始化,之前完成的函数调用中的任何垃圾都可以在那里找到。

答案 2 :(得分:3)

正如查尔斯所说,这个对象只有在它被新化之后才会终生,所以在你的新实现中设置数据是相当危险的。

此外,当您的开发人员使用Lint等工具时,很可能会抱怨成员isOnHeap未在构造函数中初始化。如果有人认为“嘿,Lint是对的,让我们在A的构造函数中初始化isOnHeap”,这将破坏你试图实现的机制。

还有第二种情况你可能没想过。假设某人写了这个:

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

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

然后你的新实现将不会被调用。然而,A的实例在堆上分配(作为MyClass实例的一部分)。

你能解释为什么你想知道堆上是否分配了什么东西?也许还有另一种更优雅的解决方案。