在c++中使用a方法而不是类自己的析构函数的任何原因都要清除
Any reasons to use a a method instead of the class own destructor to clean up in C++?
最近我在看rastertek的教程,发现他们建议使用Shutdown()
方法来清理,而不是使用类自己的析构函数。他们提到的原因是,当调用一些不安全的函数(如ExitThread()
)时,不能保证析构函数被执行。
然而,我怀疑当析构函数不能被调用时,该方法是否会被执行。的确,你总是可以在调用ExitThread()
之前调用Shutdown()
,但是为什么析构函数不能这样做呢?如果我可以在调用ExitThread()
之前做一些事情,我当然也可以调用析构函数。
在析构函数中放置清理代码是否比使用另一种方法更安全?我知道释放一些重要资源(如关闭文件)可能需要这个单独的方法来实现。但是还有其他的原因吗?因为在教程中并没有出现这种情况。
郑重声明,我知道还有一个类似的问题。
这里的问题是,虽然ExitThread(和其他类似的函数)对于C来说是一个完美的API,但对于c++代码,它会破坏堆栈展开。在析构函数中放置清理代码是否比使用另一种方法更安全?
c++的正确解决方案是确保在使用任何带有析构函数的代码中不调用ExitThread(以及类似的)。
问题:
void thread_function()
{
raii_resource r { acquire_resource() };
ExitThread();
// problem: r.~raii_resource() not called
}
关机方案:
void thread_function()
{
raii_resource r { acquire_resource() };
r.shutdown(); // release resources here
ExitThread();
// r.~raii_resource() still not called
}
关闭解决方案在客户端代码中根本不明显。就像@stefan说的,用火杀死它。
更好的解决方案(比关闭的事情):
void thread_function()
{
{ // artificial scope where RAII objects live
raii_resource r { acquire_resource() };
}
// this space does not support RAII life
ExitThread();
}
RAII在这里工作得很好,但是人工作用域不是很优雅。最重要的是,它和关闭解决方案一样不美观(它需要在客户端代码中使用不明显的技巧)。
更好(更清洁)的解决方案:
template<typename F>
void run_thread(F functor)
{
functor(); // all RAII resources inside functor; this is simple and
// obvious from client code
ExitThread();
}
将初始化移出构造函数,并将清理移出析构函数的唯一好处是,当您有一个基类框架时,您希望在这些阶段可靠地调用虚方法。
由于虚函数表在构造/析构过程中发生了变化,因此调用虚函数不会解析到最派生的实例。通过显式的Initialize
/Shutdown
方法,可以确保虚拟函数的正确调度。
请注意,这不是一个支持这种方法的答案,只是一个试图找出他们为什么建议这样做的答案!
析构函数保证在对象销毁时被调用,但是线程清理可能需要的不仅仅是对象销毁。一般来说,当您需要处理共享资源的释放、处理旧库等时,会添加清理方法。
特别是你正在处理Win32 API,它有资格作为一个古老的c风格库,考虑到ExitThread
已经存在的时间比我长…
使用这种方法,您将需要在所有应该销毁对象的情况下调用Shutdown -每次当它离开作用域时。在异常的情况下(如果使用了异常),您将无法调用它。在这些情况下,将自动调用析构函数。
"我当然也可以调用析构函数"——强烈不建议显式地调用析构函数,因为它在任何情况下都会被自动调用。除非在特殊情况下,否则应避免这种情况。如果您指的是教程中的这段代码:
System->Shutdown();
delete System;
那么我看不出有什么区别,因为delete System;
无论如何都会调用析构函数。
无论如何我更喜欢
{
// work with System
...
}
@utnapistim回答中提到的。我没有看到这种编码方式有任何缺点,而且它也是指定作用域的常用方法。即使不深入了解遗留::ExitThread
的细节,您也可以获得自动清理。这是可能的工作与WinApi与c++ RAII技术,看看例如在我的代码:多线程样例项目。初始提交使用的是WinAPI,下一次提交引入了资源包装器。你可以比较两种变体,第二种更清晰。
- 不命名构造函数和析构函数上的类型错误
- 析构函数中的"delete this"
- 如何正确取消析构函数中的 Boost deadline_timer(在多线程环境中)?
- 当两者都调用时,删除和析构函数之间的区别?
- Release() 和析构函数之间的区别?
- 读取类的析构函数中的位置时发生访问冲突
- C++ 由于类析构函数中的指针设置为 NULL 而导致的内存泄漏
- 如果引用应该保留,不删除析构函数中的指针会导致内存泄漏吗?
- 由类析构函数完成的同步
- 使用线程销毁类析构函数中的shared_ptr成员变量
- 防止派生析构函数中的 vtable 数据争用
- 析构函数中的互斥锁C++在 Python 中调用 exit() 时会导致异常
- 共享指针析构函数中的内存顺序
- 模板化子类析构函数中的隔离错误
- GCC 9.1 返回 void& 作为显式析构函数调用的结果类型。这是一个错误吗?
- 为什么我的析构函数中的异常没有触发 std::终止?
- 删除指向某个类析构函数中的对象的指针会自动将其从堆中删除吗?
- 替代基础析构函数上的虚拟函数调用
- 这是删除析构函数中的数组的正确方法吗?
- Clang修改析构函数中的返回值