为什么在运算符delete中不调用析构函数?

exp*_*inc 16 c++ delete-operator

我试图在其中召唤::delete一堂课operator delete。但是不调用析构函数。

我定义的类MyClass,其operator delete过载。全局operator delete也超载。重载operator deleteMyClass将调用重载的全局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;
}
Run Code Online (Sandbox Code Playgroud)

输出为:

Newing MyClass...
Global newing...
Constructing MyClass...
End of newing MyClass...
Constructing MyClass...
Destroying MyClass...
Deleting MyClass...
Global deleting...
End of deleting MyClass...
Run Code Online (Sandbox Code Playgroud)

实际:

只有一个调用重载之前调用析构函数operator deleteMyClass

预期:

有两个对析构函数的调用。调用重载之前的operator delete一个MyClass。另一个才叫全球operator delete

wal*_*nut 17

您正在滥用operator newoperator delete。这些运算符是分配和释放函数。他们不负责构造或破坏对象。它们仅负责提供用于放置对象的内存。

这些函数的全局版本是::operator new::operator delete::new::delete是新的/删除表达式,因为是new/ delete从那些不同的,在那::new::delete将绕过类特定operator new/ operator delete过载。

new / delete-expressions构造/销毁分配/解除分配(通过调用适当的命令operator newoperator delete在构造之前或在销毁之后)。

由于您的重载仅负责分配/取消分配部分,因此应调用::operator new::operator delete而不是::new::delete

deletedelete myClass;负责调用析构函数。

::delete p;不调用析构函数,因为它p具有类型void*,因此表达式无法知道要调用的析构函数。::operator delete尽管将a void*操作数用作delete-expression的格式不正确,但它可能会调用您的replaced 来分配内存。

::new MyClass();调用您替换::operator new的对象来分配内存并在其中构造一个对象。返回该对象的指针,指向中void*的new-expression MyClass* myClass = new MyClass();,它将在该内存中构造另一个对象,从而在不调用其析构函数的情况下结束前一个对象的生存期。


编辑:

感谢@MM对这个问题的评论,我意识到void*as的操作数::delete实际上是格式错误的。([expr.delete] / 1)但是,主要的编译器似乎已决定仅对此警告,而不是错误。它是由前形成不良的,使用::deletevoid*就已经不确定的行为,看到了这个问题

因此,您的程序格式错误,如果仍然可以编译,则不能保证该代码确实执行了我上面描述的操作。


正如@SanderDeDycker在其答案下方所指出的那样,您还具有未定义的行为,因为通过在内存中构造一个已经包含MyClass对象的对象而不先调用该对象的析构函数,您就违反了[basic.life] / 5,如果程序取决于析构函数的副作用。在这种情况下,printf析构函数中的语句具有这种副作用。


San*_*ker 14

您特定于类的重载操作不正确。在输出中可以看到:构造函数被调用两次!

在特定于类的中operator new,直接调用全局运算符:

return ::operator new(size);
Run Code Online (Sandbox Code Playgroud)

同样,在特定于类的中operator delete,执行以下操作:

::operator delete(p);
Run Code Online (Sandbox Code Playgroud)

operator new有关更多详细信息,请参见参考页。