举一个小例子,我试图找出是否在堆上分配了一个变量:
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(对堆栈对象有警告)。这是不明确的定义 做这样的手术?
答案 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实例的一部分)。
你能解释为什么你想知道堆上是否分配了什么东西?也许还有另一种更优雅的解决方案。