在c++中使用a方法而不是类自己的析构函数的任何原因都要清除

Any reasons to use a a method instead of the class own destructor to clean up in C++?

本文关键字:析构函数 自己的 任何原 清除 c++ 方法      更新时间:2023-10-16

最近我在看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,下一次提交引入了资源包装器。你可以比较两种变体,第二种更清晰。