是否在重载的运算符删除函数中隐式调用了析构函数?

Is destructor called implicitly in a overloaded operator delete function?

我有 class Item 定义了自己的 operator new 和 operator delete 如下:

class Item
{
public:
    Item(const std::string &s):msg(s)
    {
        std::cout<<"Ctor: "<<msg<<std::endl;
    }
    static void* operator new(size_t size, int ID, const std::string &extra)
    {
        std::cout<<"My Operator New. ID/extra: "<<ID<<"/"<<extra<<std::endl;
        return ::operator new(size);
    }
    static void operator delete(void* p)
    {
        std::cout<<"My Operator Delete"<<std::endl;
        return;
    }
    ~Item()
    {
        std::cout<<"Destructor: "<<msg<<std::endl;
    }
    void Print()
    {
        std::cout<<"Item::msg: "<<msg<<std::endl;
    }
private:
    std::string msg;
};

我创建了一个这种类型的对象,使用一个placement new然后删除如下:

int main()
{
    Item *pI=new(1,"haha")Item("AC Milan");
    std::cout<<"before delete"<<std::endl;
    delete pI;
    std::cout<<"after delete"<<std::endl;
    return 0;
}

输出为:

My Operator New. ID/extra: 1/haha
Ctor: AC Milan
before delete
Destructor: AC Milan
My Operator Delete
after delete

如您所见,delete pI 调用了我自己的删除函数,其中除了输出日志外什么也没做。但是,从输出来看,Item 的析构函数在 delete pI 中调用,而我自己的删除函数中并未调用它。

所以在这种情况下,析构函数会在重载的删除函数中隐式调用?

So in this case, destructor would be called implicitly in a overloaded delete function?

是的。对于delete expression,(1)首先调用析构函数,然后(2)调用相应的operator delete;在此阶段将执行名称查找和重载解析。

If expression is not a null pointer, the delete expression invokes the destructor (if any) for the object that's being destroyed, or for every element of the array being destroyed (proceeding from the last element to the first element of the array).

After that, unless the matching new-expression was combined with another new-expression (since C++14) the delete expression invokes the deallocation function, either operator delete (for the first version of the expression) or operator delete[] (for the second version of the expression).

So in this case, destructor would be called implicitly in a overloaded delete function?

析构函数和重载的删除运算符以复杂的方式交互。当你说

delete pI;

这实际上编译为对析构函数的调用,并且没有引用 "delete"。

你的析构函数在 VTable(如果是虚拟的)中有 (GCC) 两个实例,或者 (MSVC) 一个布尔参数。 instances / bool 用于确定如此编译的析构函数应该是删除还是非删除析构函数调用。您可以猜到,删除析构函数然后负责为您调用 link-time 运算符 delete。如果你写了

pI->~Item();

你会编译成 运行 非删除析构函数(另一个 vtable 条目,或者翻转布尔值)。

因为你的析构函数是非虚拟的,所以它可能都是内联的。尽管这种行为与此没有区别。这就是如果你的析构函数是虚拟的,删除将为你实际拥有的任何类型调用正确的删除,而不是你调用 delete 的指针类型的删除。

重载的 operator delete() 函数未调用析构函数。

但是,delete 表达式(在您的例子中,delete pI)的作用是首先调用对象的析构函数,然后调用 operator delete() 的适当重载。