因此,我正在尝试创建一个具有该类型基类的默认释放函数的基类。根据我如何删除派生对象的方式,我会看到不同的行为,也许任何人都可以弄清楚为什么在以下未调用自定义解除分配的情况下,我的覆盖无法正常工作:
#include <iostream>
struct B {
void operator delete(void* ptr) {
std::cout << "B's operator delete" << std::endl;
::operator delete(ptr);
}
};
struct D : B {
};
template<typename T>
class E {
public:
E(T* inst) {
//delete inst; // invokes the operator delete override
T::operator delete(inst); // invokes the operator delete override
//operator delete(inst); // does not invoke the operator delete override
}
};
int main() {
D* dp = new D();
E<D>* ep = new E<D>(dp);
delete ep;
}
我猜测没有T ::的最后一次尝试(调用操作符删除)使用了全局释放函数而不是我的替代,但是为什么在delete inst
正常工作而不必指定T的情况下: :?
我希望所有三个语句实际上都可以为该对象调用删除操作符(如果该对象已被覆盖)。可以通过任何方式控制它,还是可以按照C ++ ABI正确地控制它?
答案 0 :(得分:1)
对此并不完全确定,但是我认为这是Stephen Dewhurst在 C ++ Gotchas #23中称为“运算符查找异常”的情况。他将显式形式称为“函数调用语法”,以调用运算符重载,并显示了一个示例,其中,在名称查找方面,中缀语法和函数调用语法有所不同。
class X { public: X &operator %( const X & ) const; // ... }; X &operator %( const X &, int ); void X::f() { X &anX = *this; anX % 12; // OK, non-member operator %( anX, 12 ); // error! }
函数调用语法的使用遵循搜索函数名称的标准查找顺序。对于成员函数
X::f
,编译器将首先在类X
中查找名为operator %
的函数。找到名称后,就不会继续在外部作用域中寻找名为operator %
的其他功能。
让我们将其应用于operator delete
场景。
template<typename T>
class E {
public:
E(T* inst) {
// delete inst; // Ok, infix call syntax
// T::operator delete(inst); // Ok, qualified lookup enforced
operator delete(inst); // The "anomaly"! This finds the operator delete for E
}
};
在这里,名称查找在operator delete
提供的E
处停止,这是默认的全局实现。您可以通过向operator delete
添加E
成员函数来验证这一点。
答案 1 :(得分:1)
一个类的释放函数(operator delete
)是该类[class.free]/5的静态成员函数。 delete
表达式具有特殊的性质,对释放函数的查找还考虑了特定于类的释放函数[expr.delete] / 9 [class.free]/4,这就是为什么
delete inst;
确实会调用正确的释放函数。当您只是手动书写时
operator delete(inst);
然后,实际上只不过是调用名为operator delete
的函数族。这样的调用只会经过正常的重载解析,除非明确给出 qualified-id (即
T::operator delete(inst);
确实可以,为什么它也可以正常工作...