我找到了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 ++规范,但我必须承认,我有点不知所措,所以任何帮助都会受到赞赏。
答案 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
运算符没有隐式虚拟性 - 所有虚拟都在析构函数调用结束时结束。