【问题标题】:Returning a different class pointer by overloaded operator new in a class通过类中的重载运算符 new 返回不同的类指针
【发布时间】:2013-08-27 13:19:02
【问题描述】:

假设我有这个示例代码:

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 中测试):

  • 构造函数
  • B 构造函数
  • 构造函数

前两个构造函数调用是因为重载的 operator new 函数中的new B()。 但随后将在函数返回的指针上再次调用 A 构造函数,因为重载的 operator new 应该返回指向空闲内存的指针(不创建对象),因此再次调用构造函数。

如果我在此示例中使用指针 b,这是未定义的行为吗?

【问题讨论】:

  • 你应该返回原始内存。运算符 new 和 new 表达式(也将调用 ctor)之间存在差异
  • A 重载newdelete 运算符来创建/销毁B 对象真是太可怕了!
  • 这里出了点问题。如发布的那样,代码具有无限递归。 BA 继承operator new,所以A::operator new 中的new B() 表达式调用A::operator new

标签: c++ pointers constructor new-operator


【解决方案1】:

您发布的代码具有无限递归,因为您调用 A::operator new 来自A::operator new;班级BA 继承operator new

除此之外,你对编译器撒谎,导致 undefined 行为。在new A 之后,你有一个指向一个对象的指针 类型是A。您可以合法地将其地址转换为B*,但是 你可以用B* 做的就是将它转换回A*; 其他任何事情都是未定义的行为。

目前尚不清楚您要使用A::operator new 中的new B 实现什么目标。编译器将考虑任何内存 从operator new 作为原始内存返回;在这种情况下,它 将在其中构造一个A 对象,从那时起,所有 你有一个A 对象。任何将其用作B 的尝试 对象是未定义的行为。 (当然,如果你真的 需要破坏A::operator new中创建的B,不能 因为你已经覆盖了它。

最后:你不必将operator new 声明为static; 暗示是,不写static 是惯用的 这个案例。同样,将new B 的结果分配给 一个void*,转换是惯用的,不是惯用的 使其明确。 (也最好避免C风格 强制转换,因为它们隐藏了太多错误。)

【讨论】:

  • 感谢您的详细解答。我不想使用这段代码,只是想知道这是否是未定义的行为。
【解决方案2】:

一般来说,operator new() 不应该创建一个对象,它应该为一个对象创建空间。您的代码将用A 对象覆盖B 对象,然后将其用作B 对象,是的,这将是“未定义的”(可能在“将对象转换为不同的对象”下的文档中进行了介绍类型,它最初不是创建的)。

这在这种特殊情况下似乎可以工作,但如果B 的构造函数更复杂(例如B 中有虚函数),它将立即无法正常工作。

如果你想为一个对象分配内存,你可以这样做:L

void* A::operator new(size_t sz)
{
   void* ptr = (void*)::new unsigned char[sz];
   return ptr;
}

现在您不会为同一个对象调用两个不同的构造函数!

【讨论】:

  • 只是一个笨蛋,但尽管他声称,他的代码从未为任何事情调用任何构造函数;在到达第一个构造函数调用之前,它会进入无限递归。
  • 嗯,我在想它可能会这样做,因为新的(我添加的)前面缺少 ::。
  • 这是一个很好的预防措施。尽管在您的情况下,没有必要,因为 operator new 是在正在构造的对象的范围内查找的(或者在 new[] 的情况下是元素类型),而不是在调用发生的范围内。 (当然,当我想要原始内存时,我会直接调用::operator new( sz )。我似乎让目的更明确。在这种情况下,你需要::,因为这只是一个日常函数调用。 )
【解决方案3】:

operator new 的约定只是内存分配,稍后由 new-expression (通过调用构造函数)或如果直接调用运算符则由程序代码完成初始化。

你想做的事不能做,不能那样做。您可以重新设计以使用工厂成员函数,该函数将返回指向 B 对象的指针,例如...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-18
    • 2015-08-12
    • 2012-09-08
    • 1970-01-01
    • 2013-02-03
    • 2011-07-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多