为什么在 operator delete 中不调用析构函数?
Why is the destructor not called in operator delete?
我试图在 operator delete
中为 class 调用 ::delete
。但是没有调用析构函数。
我定义了一个 class MyClass
其 operator 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
.
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 new
和 operator delete
。这些运算符是分配和释放函数。他们不负责构建或销毁对象。他们只负责提供放置对象的内存。
这些函数的全球版本是 ::operator new
和 ::operator delete
。
::new
和 ::delete
是 new/delete-expressions,new
/delete
也是,不同之处在于 ::new
和 ::delete
将绕过 class-特定的 operator new
/operator delete
重载。
new/delete-expressionsconstruct/destruct和allocate/deallocate(通过在构造前调用合适的operator new
或operator 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
参考页。
我试图在 operator delete
中为 class 调用 ::delete
。但是没有调用析构函数。
我定义了一个 class MyClass
其 operator 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
.
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 new
和 operator delete
。这些运算符是分配和释放函数。他们不负责构建或销毁对象。他们只负责提供放置对象的内存。
这些函数的全球版本是 ::operator new
和 ::operator delete
。
::new
和 ::delete
是 new/delete-expressions,new
/delete
也是,不同之处在于 ::new
和 ::delete
将绕过 class-特定的 operator new
/operator delete
重载。
new/delete-expressionsconstruct/destruct和allocate/deallocate(通过在构造前调用合适的operator new
或operator 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
参考页。