c++ RAII析构函数异常

c++ RAII destructor exception

本文关键字:异常 析构函数 RAII c++      更新时间:2023-10-16

据我所知,RAII是指在tor中获取资源并在tor中释放资源。

tor获取一些资源,并且可能失败,导致异常。dr会释放资源,也会失败,但是dr的exception是foobar,所以没有异常。

class A {
  A() throw(Ex) { // acquire resources }
  ~A() throw() { // release resources }
}

因此,如果类A的用户应该意识到A的反初始化中发生的错误,我可以将反初始化外包给一个抛出的函数,该函数从一个吞下异常的医生调用:

class A {
  A() throw(Ex) { // acquire resources }
  ~A() throw() { try {Release(); } catch(...) {} }
  void Release() throw(Ex) { // release resources }
}

这样,如果用户想要释放错误的反馈,可以调用Exit(),或者当A超出作用域时(例如,在使用A时发生其他异常),可以忽略让医生做它的工作。

为了防止多次执行Exit()(首先由user显式执行,后来由dr间接执行),我必须添加init-status:

class A {
  bool init;
  A() throw(Ex) { init = true; // acquire resources }
  ~A() throw() { try {Release(); } catch(...) {} }
  void Release() throw(Ex) {
    if(!init) return;
    init = false;
    // release resources
   }
}

是否有更好的方法来做到这一点,或者我是否必须在每次资源释放失败并且我想知道它时实现该模式?

释放资源不应该有任何失败的范围。例如,释放内存当然可以以一种不会抛出异常的形式实现。RAII的目的是清理资源,而不是处理大量清理导致的错误。

显然,有些清理操作可能会失败。例如,关闭一个文件可能会失败,例如,因为关闭它将刷新内部缓冲区,而这可能会失败,因为文件正在写入的磁盘已满。如果清理操作失败,可能应该有一个合适的释放操作,如果用户对报告清理过程中的错误感兴趣,他们应该使用这个方法:在正常路径中,将有机会处理任何错误。

当释放作为处理现有错误的一部分进行时,即抛出异常且未达到释放操作,任何异常都需要被析构函数吃掉。可能有一些处理方法,例如,记录抛出的异常消息,但异常不应该逃离析构函数。

总的来说,我认为如果您一直遵循RAII指导方针,那么您肯定需要在析构函数中抛出异常。

例如:关闭文件,释放互斥锁,关闭套接字连接,取消文件映射,关闭通信端口,回滚数据库事务等。在RAII破坏中做了太多的工作将会失败。

我们应该如何处理这些失败?在RAII析构函数中,我们几乎没有足够的信息来知道如何正确处理这些故障。我们只能选择忽略它或将其传递给上层。

但是,如果这些错误可以安全地忽略,为什么操作系统提供的api,如close, munmap, pthread_mutex_destroy等都返回错误码给我们?他们能简单地返回空吗?

所以我们最终不得不写一个析构函数:

CResource::~CResource() noexcept(false)
{
    if (-1 == close(m_fd))
    {
        // ...
        if (std::uncaught_exception())
        {
            return;
        }
        throw myExp(m_fd, ...);
    }
    // ...
}

当然,除了抛出异常,我们也可以选择自己的向上传播方法。例如,让上层组件为析构时可能抛出的每种类型注册一个回调方法,或者维护一个全局队列来存储和传递这些异常,等等。

但很明显,这些替代方案更笨拙,更难以使用。这相当于您自己重新实现异常机制。