为什么delete运算符必须是静态的?

时间:2010-02-16 13:31:58

标签: c++ operator-overloading

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

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

据我所知,当调用delete运算符时,该对象已被销毁,因此不存在“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

应该调用一个删除操作符(默认值),因为它是静态的,并且在编译时不知道(除了这里的小事),delete-operator是正确的。

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

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

我开始查看C ++规范,但我必须承认,我有点不知所措,所以任何帮助都会受到赞赏。

2 个答案:

答案 0 :(得分:17)

语言规则中的答案实际上是12.5 [class.free]。

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

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

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

答案 1 :(得分:1)

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

这就是为什么当你执行delete ptr; delete运算符时,对于要删除的对象的实际最派生类,它总是只被调用一次,并且从哪个类推导出它的数据。如果存在虚拟析构函数,则为vtable;如果没有虚拟析构函数,则为指针类型。这就是为什么delete运算符没有隐式虚拟性 - 所有虚拟都在析构函数调用结束时结束。