是否可以对全局运算符“new”和“delete”执行回调



我正在做的是:

#include <iostream>
using namespace std;
class Test
{
public:
    int i;
    Test();
    Test(int x);
    ~Test();
    static void operator delete(void * t);
};
Test::Test() : i(1) { cout << "constructing" << endl; }
Test::Test(int x) : i(x) { cout << "constructing w/ arg" << endl; }
Test::~Test() { cout << "destructing" << endl; }
Test::operator delete(void *self)
{
    cout << "deleting" << endl;
    ((Test *) t)->~Test(); // call destructor since it isnt called otherwise
    ::delete(t); // actually delete memory
}
template <typename T> // too lazy to figure out correct type
void callback(Test *t, T fn)
{
    (fn)(t); // delete operator is implicitly static so this syntax is correct
}
int main()
{
    Test *t = new Test();
    callback(t, &Test::operator delete); // deletes t
}

我注意到,除非为我的类重载operator delete,否则前面的代码段将无法编译。如果包含它,它将按预期编译和工作(首先调用构造函数,然后调用重载delete,然后调用析构函数,每一个都只调用一次)。

我曾想过传递全局删除运算符::operator delete,但这也不起作用(我得到了一个未解析的重载函数调用)。我可以打电话给它,而不必试图弄清楚它的地址。

在不定义自己的::operator delete过载的情况下,我所做的事情是否可能?

我知道基本上没有一个用例需要我使用这样的东西。我知道::operator delete不是一个通用的东西,它不调用析构函数。。。。

全局运算符new/delete是重载函数-您需要转换为正确的函数指针类型:

callback(t, static_cast<void(*)(void*)>(::operator delete));

编辑:我正在扩展我的答案来澄清一些事情。

的确,析构函数不是由全局operator delete调用的。它只是负责将内存返回到运行时的释放函数。调用析构函数是删除表达式:的工作

Delete表达式首先调用其指针参数指向的对象(如果我们使用数组形式,则为一个或多个对象)的析构函数,然后调用释放函数(如果要删除的指针是类类型的,则它首先在该类的作用域中查找它,如果找不到,则调用全局函数)。

另一个需要注意的重要事项是,在delete表达式中使用void*指针是未定义的行为,这就是您在重载中所做的:

cout << "deleting" << endl;
((Test *) self)->~Test();
::delete(self); // this is delete expression using void*, UB

您不是在调用全局operator delete,因此您需要说::operator delete(t);。您有一个delete表达式,作用域解析运算符只是告诉它不要在类作用域内查找解除分配函数。

如果您将其更改为此::delete( (Test*) self);,您将看到destructing被打印两次,再次是UB。

总而言之,不要在operator delete内部调用析构函数,这不是它的工作。

最新更新