【问题标题】:Why is the delete operator required to be static?为什么删除操作符必须是静态的?
【发布时间】:2010-02-16 13:31:57
【问题描述】:

我找到了this one question asking the same thing,但是只回答了“新”部分,所以又来了。

为什么删除操作符必须是静态的?不知何故,这没有意义。 new 运算符非常有意义,就像构造函数不能是虚拟的一样,new 运算符也不能。但是,当您使用继承时,析构函数可以(并且应该)是虚拟的,以便允许销毁(通过多态性)用作基类的对象。

我知道,当调用删除操作符时,对象已经被销毁,所以不存在“this”。然而,使用与虚拟析构函数相同的推理,让 delete 运算符与创建对象的 new 运算符匹配仍然是有意义的。

这就是我的意思

class A
{
  public:
    virtual ~A() {}
};

class B : public A
{
  public:
    void* operator new (size_t sz);
    void  operator delete (void* ptr, size_t sz);
};

现在如果我们这样做

A *ptr = new B();
delete ptr; // <-- fail

应该调用 A 的删除操作符(默认),因为它是静态的,并且在编译时不知道(除了这里的简单情况)哪个删除操作符是正确的。

但是,我用上面的代码做了一个小测试程序(只是在 new/delete 运算符中使用 malloc/free,在 delete 中使用 print 语句),并使用 g++ 编译它。运行它出乎意料地在 B 的删除运算符中产生了输出。

我的(真正的)问题是:删除操作符是否存在某种隐含的“虚拟性”?它只是在没有这个指针的意义上是静态的吗?还是这只是一个 g++ 功能?

我开始查看 C++ 规范,但我必须承认,我对它有点不知所措,因此感谢任何帮助。

【问题讨论】:

    标签: c++ operator-overloading


    【解决方案1】:

    语言规则中的答案确实在 12.5 [class.free] 中。

    如果您通过指向基类的指针进行删除,那么析构函数必须是虚拟的,否则您会得到未定义的行为。否则,实现必须确定被删除对象的动态类型。

    12.5/4 表示当delete 没有以:: 为前缀时,释放函数是通过在动态类型的虚拟析构函数的上下文中查找delete 来确定的。这确保了类似虚拟的查找,即使 operator delete 始终是 static 成员函数。

    原始分配和解除分配在概念上发生在对象的生命周期之外,因此在调用解除分配函数时,不再有对象提供虚拟查找机制,但查找规则确保 operator delete 具有动态(虚拟精简版!)查找机制。这意味着 operator delete 可以合理地为 static 而不会失去与原始对象的动态类型的联系。

    【讨论】:

    • 在 5.3.5 以及 3.7.3 和 12.5 中也有相关内容。虽然标榜为参考作品,但您似乎必须从头到尾阅读它,否则您永远不会知道是否有一小段与您正在查找的内容相关的部分与您实际所在的位置完全不同看着。
    【解决方案2】:

    delete 运算符仅用于释放内存,并且为整个最派生的类对象释放内存 - 在一个操作中 - 与 new 运算符完全相同,它被分配给整个最派生的类对象类对象 - 作为参数传递给new Class 构造的类对象。

    这就是为什么当你执行delete ptr; 时,delete 操作符对于被删除对象的实际派生最多的类总是只调用一次,并且如果如果没有虚拟析构函数,则存在虚拟析构函数或指针的类型。这就是为什么 delete 运算符没有隐含的虚拟性 - 所有虚拟性都在析构函数调用点结束。

    【讨论】:

    • @roe:最派生类运算符 delete 的调用方式与最派生运算符 new 的调用方式相同。因此,如果您有不同的银行,您将获得从不同银行分配的不同类的对象,但仅作为整个对象,永远不会发生子对象来自一个银行而对象的派生类部分来自另一个银行。
    • 所以要使用的运算符是从实际对象是什么类推导出来的(即它寻找一个虚拟表,或者它如何工作),听起来对运算符有一些隐含的虚拟性?假设我有一个派生自 B 的 C 类,A* ptr = new C; delete ptr; 仍然调用 B 的删除运算符,这听起来对我来说几乎是虚拟的。很抱歉评论垃圾邮件,我只是想绕开它。
    • @roe: delete 和 new 操作符被继承。由于您在 B 类中重载了它们,因此 C 类也将使用它们。在这里查看漂亮的图表:msdn.microsoft.com/en-us/library/c5at8eya.aspx
    • 我想我想说的是,对象本身必须携带用于该对象的删除运算符的信息,即删除运算符是隐式虚拟的。你碰巧知道我可以在规范中找到这个吗?
    • @roe:不,我不知道它在规范中的位置。我想这里的关键点是使用虚拟析构函数——如果你有它们,其余的都可以工作,否则就看情况了。
    猜你喜欢
    • 2014-05-27
    • 2019-02-06
    • 2012-04-11
    • 1970-01-01
    • 1970-01-01
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 2017-08-23
    相关资源
    最近更新 更多