为什么在 operator delete 中不调用析构函数?

Why is the destructor not called in operator delete?

我试图在 operator delete 中为 class 调用 ::delete。但是没有调用析构函数。

我定义了一个 class MyClassoperator delete 已被重载。全局 operator delete 也超载了。 MyClass 的重载 operator delete 会调用重载的全局 operator delete.

class MyClass
{
public:
    MyClass() { printf("Constructing MyClass...\n"); }
    virtual ~MyClass() { printf("Destroying MyClass...\n"); }

    void* operator new(size_t size)
    {
        printf("Newing MyClass...\n");
        void* p = ::new MyClass();
        printf("End of newing MyClass...\n");
        return p;
    }

    void operator delete(void* p)
    {
        printf("Deleting MyClass...\n");
        ::delete p;    // Why is the destructor not called here?
        printf("End of deleting MyClass...\n");
    }
};

void* operator new(size_t size)
{
    printf("Global newing...\n");
    return malloc(size);
}

void operator delete(void* p)
{
    printf("Global deleting...\n");
    free(p);
}

int main(int argc, char** argv)
{
    MyClass* myClass = new MyClass();
    delete myClass;

    return EXIT_SUCCESS;
}

输出为:

Newing MyClass...
Global newing...
Constructing MyClass...
End of newing MyClass...
Constructing MyClass...
Destroying MyClass...
Deleting MyClass...
Global deleting...
End of deleting MyClass...

实际:

在调用MyClass的重载operator delete之前只有一次析构函数调用。

预计:

有两次对析构函数的调用。调用 MyClass 的重载 operator delete 之前的一个。调用全局之前的另一个operator delete.

参见CPP Reference

operator delete, operator delete[]

Deallocates storage previously allocated by a matching operator new. These deallocation functions are called by delete-expressions and by new-expressions to deallocate memory after destructing (or failing to construct) objects with dynamic storage duration. They may also be called using regular function call syntax.

删除(和新建)只负责'memory management'部分。

所以很明显,析构函数只被调用一次——清理对象的实例。它会被调用两次吗,每个析构函数都必须检查它是否已经被调用。

您误用了 operator newoperator delete。这些运算符是分配和释放函数。他们不负责构建或销毁对象。他们只负责提供放置对象的内存。

这些函数的全球版本是 ::operator new::operator delete::new::delete 是 new/delete-expressions,new/delete 也是,不同之处在于 ::new::delete 将绕过 class-特定的 operator new/operator delete 重载。

new/delete-expressionsconstruct/destructallocate/deallocate(通过在构造前调用合适的operator newoperator delete或销毁后)。

由于您的重载仅负责 allocation/deallocation 部分,因此它应该调用 ::operator new::operator delete 而不是 ::new::delete

delete myClass;中的delete负责调用析构函数

::delete p; 不调用析构函数,因为 p 的类型为 void*,因此表达式不知道要调用哪个析构函数。它可能会调用你替换的 ::operator delete 来释放内存,尽管使用 void* 作为 delete-expression 的操作数是错误的(见下面的编辑) .

::new MyClass(); 调用你替换的 ::operator new 来分配内存并在其中构造一个对象。指向此对象的指针作为 void* 返回到 MyClass* myClass = new MyClass(); 中的新表达式,然后它将在该内存中构造 另一个对象 ,结束该对象的生命周期上一个对象而不调用其析构函数。


编辑:

感谢 @M.M 对问题的评论,我意识到 void* 作为 ::delete 的操作数实际上是不正确的。 ([expr.delete]/1) However, the major compilers seem to have decided to only warn about this, not error. Before it was made ill-formed, using ::delete on a void* had already undefined behavior, see .

因此,您的程序格式不正确,如果代码仍然能够编译,您无法保证它确实执行了我上面描述的操作。


正如@SanderDeDycker 在他的回答下方所指出的,您也有未定义的行为,因为通过在内存中构造另一个已经包含 MyClass 对象的对象而不首先调用该对象的析构函数,您违反了 [basic.life]/5 如果程序依赖于析构函数的副作用,则禁止这样做。在这种情况下,析构函数中的 printf 语句会产生这样的副作用。

您的 class 特定重载未正确完成。这可以在您的输出中看到:构造函数被调用了两次!

在class-specific operator new中,直接调用全局运算符:

return ::operator new(size);

同样,在 class-specific operator delete 中,执​​行:

::operator delete(p);

有关详细信息,请参阅 operator new 参考页。