假设我有这个示例代码:
class A
{
public:
static void* operator new(size_t sz);
private:
int xA;
float yA;
};
class B : public A
{
private:
int xB;
float yB;
};
void* A::operator new(size_t sz)
{
void* ptr = (void*)new B();
return ptr;
}
int main()
{
B* b = (B*) new A();
// Use b ..
delete b;
return 0;
}
这里将按顺序调用构造函数(在VS2012中测试):
前两个构造函数调用是因为重载运算符new函数中的new B()
。
但是然后将在函数返回的指针上再次调用A构造函数,因为重载的operator new应该返回一个指向空闲内存的指针(不创建对象),因此再次调用构造函数。
如果我在这个例子中使用指针b
,这个未定义的行为是什么?
答案 0 :(得分:1)
通常,operator new()
不应该创建对象,它应该为对象创建空间。您的代码将使用B
对象覆盖A
对象,然后将其用作B
对象,是的,这将是“未定义”(可能在“将对象转换为最初未创建的其他类型的对象。
这似乎适用于这种特殊情况,但如果B
的构造函数更复杂(例如B
中有虚函数),则会立即无法正常工作。
如果要为对象分配内存,可以执行以下操作:L
void* A::operator new(size_t sz)
{
void* ptr = (void*)::new unsigned char[sz];
return ptr;
}
现在你没有为同一个对象调用两个不同的构造函数!
答案 1 :(得分:1)
operator new
的契约只是内存分配,稍后由 new-expression (通过调用构造函数)或程序代码完成初始化(如果直接调用运算符)
你想要做的事情是不能做到的,不是那样的。您可以重新设计使用工厂成员函数,该函数将返回指向B
对象的指针,例如......
答案 2 :(得分:1)
你发布的代码有无限的递归,因为你打电话
来自A::operator new
内的A::operator new
;班级B
从operator new
继承A
。
除此之外,你撒谎到编译器,导致未定义
行为。在new A
之后,你有一个指向一个对象的指针
类型为A
。您可以合法地将其地址转换为B*
,但是
您可以使用B*
将其转换回A*
;
其他任何事都是未定义的行为。
目前尚不清楚您在new
B
中使用A::operator new
尝试实现的目标。编译器会考虑任何内存
从operator new
作为原始记忆返回;在这种情况下,它
将在其中构建一个A
对象,从那时起,所有
你有一个A
对象。任何将其用作B
的尝试
对象是未定义的行为。 (当然,如果你真的
需要破坏B
中创建的A::operator new
,你不能
因为你已经覆盖了它。
最后:您不必将operator new
声明为static
;
它隐含着,并且不写static
而不是惯用语
这个案例。同样,在将new B
的结果分配给
一个void*
,转换是惯用的,而不是惯用的
使其明确。 (最好避免使用C风格
演员,因为他们隐藏了太多错误。)