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

Posted

技术标签:

【中文标题】是否在重载的运算符删除函数中隐式调用析构函数?【英文标题】:Is destructor called implicitly in a overloaded operator delete function? 【发布时间】:2017-04-04 16:10:30 【问题描述】:

我有 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;
;

我通过使用放置 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 中被调用,而我自己的删除函数中没有调用。

那么在这种情况下,析构函数会在重载的删除函数中被隐式调用吗?

【问题讨论】:

我使用的是 g++ 4.9.3 newdelete 操作员都是一样的。您也不会在重载的new 运算符中调用构造函数,它仍然会被调用。使用 newdelete 运算符时,始终会调用构造函数和析构函数。 内存分配和对象创建/销毁是两个不同的东西。重载分配函数只影响前者;后者完全由核心语言规则控制。 有趣的是 MSVC2015 发出警告:warning C4291: 'void *Item::operator new(std::size_t,int,const std::string &amp;)': no matching operator delete found; memory will not be freed if initialization throws an exception @Someprogrammerdude 我认为构造函数会被我自己的 operator new 调用的全局 operator new 调用 【参考方案1】:

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

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

如果 expression 不是空指针,则 delete 表达式为正在销毁的对象或正在销毁的数组的每个元素(从数组的最后一个元素继续到第一个元素)调用析构函数(如果有) )。

之后,除非匹配的新表达式与另一个新表达式(C++14 起)组合,否则删除表达式会调用释放函数,operator delete(对于表达式的第一个版本)或 @987654324 @(用于表达式的第二个版本)。

【讨论】:

【参考方案2】:

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

但是,delete 表达式(在您的情况下为 delete pI)具有首先调用对象的析构函数,然后调用适当的 operator delete() 重载的效果。

【讨论】:

【参考方案3】:

那么在这种情况下,析构函数会在重载的删除函数中被隐式调用吗?

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

delete pI;

这实际上编译为您的析构函数的调用,并且没有提及“删除”。

您的析构函数在 VTable(如果是虚拟的)中有 (GCC) 两个实例,或者 (MSVC) 有一个布尔参数。实例/布尔值用于确定如此编译的析构函数是删除还是非删除析构函数调用。如您所料,删除析构函数随后负责为您调用链接时运算符 delete。如果你写了

pI->~Item();

您将编译为运行非删除析构函数(另一个 vtable 条目,或翻转布尔值)。

由于您的析构函数是非虚拟的,因此它可能都是内联的。但是,这种行为将与此无法区分。这就是如果你的析构函数是虚拟的,delete 会为你实际拥有的任何类型调用正确的 delete,而不是你调用 delete 的指针类型的 delete。

【讨论】:

为了完整起见,还有第三个析构函数,它用于破坏你的类而不是你的虚拟基础。这与本次讨论无关,因为您没有虚拟基地。

以上是关于是否在重载的运算符删除函数中隐式调用析构函数?的主要内容,如果未能解决你的问题,请参考以下文章

当我调用一个简单的函数时,析构函数被调用

在C++中,啥是运算符重载?啥是虚函数?

构造函数和析构函数能不能被继承

vc9 和 gcc 之间的不同析构函数行为

c++单链表构造函数运算符重载析构函数增删查改等

C++类和对象(构造函数析构函数拷贝构造函数赋值运算符重载Const成员)详细解读