首页 > 解决方案 > 人们如何在异常退出之前释放所有内存?

问题描述

由于程序错误而退出时,是否有一种简单的方法可以释放所有内存?我不想释放每个分配,因为很难跟踪所有分配。使用 C++ 的经验丰富的开发人员如何解决这个问题?

标签: c++memory

解决方案


RAII 是一种技术,其中所有资源都由具有析构函数的对象拥有,但直接存在于自动(堆栈)和静态/全局存储中的对象除外。

当发生未处理的异常时,C++ 保证您的程序将展开堆栈,依次销毁堆栈上的每个对象。

当您退出 main 时,C++ 保证静态/全局存储持续时间的对象也会被销毁。

做得好,你分配的每一个资源都会被清理干净。

这需要一些纪律,而且通常是值得的。

现在,您可以依靠现代操作系统的回收内存和文件处理程序在运行时获取的资源这一事实,然后退出程序。

这要容易得多。但它有严重的局限性。

并非每个程序都有由操作系统清理的资源。如果您正在与服务器协商某些资源,那么您的操作系统清理将不起作用。如果您创建某种命名管道,您的操作系统清理将不起作用。如果你制作了一些临时文件,你的操作系统清理将不起作用。

此外,退出程序的一部分很像退出整个程序。因此,在每个部分进行适当的 RAII 清理会导致整个程序在关闭时自行清理。

RAII 是“资源分配是初始化”。最简单的情况是 a std::unique_ptr<T>,您可以在其中执行以下操作:

std::unique_ptr<int> pint = std::make_unique<int>(3);

现在pint有一个指向已分配的指针,int其值为3. 它的所有权可以移动到另一个unique_ptr(甚至是一个shared_ptr),当拥有内存的智能指针超出范围时,内存会被回收。

这可能发生在包含对象pint被销毁,或者pint超出范围(因为它是一个自动存储变量(在堆栈上)),或者在pint.

更高级的版本需要编写自己的析构函数。

将 C 风格的资源管理升级到 C++ 风格的 RAII 的一个好工具是scope_guard,它在销毁时运行任意代码。(确保破坏代码不能抛出)。

template<class F>
struct scope_guard {
  F f;
  bool bDoIt = true;
  scope_gaurd( F in ):f(std::move(in)) {}

  ~scope_guard() { run_now(); }
  void skip() { bDoIt = false; }
  bool run_now() {
    if (!bDoIt) return false;
    f();
    bDoIt = false;
    return true;
  }
  scope_guard(scope_guard const&)=delete;
  scope_guard(scope_guard && o):f(std::move(o.f)), bDoIt(o.bDoIt) {
    o.bDoIt = false;
  }
};
using storable_scope_guard = scope_guard<std::function<void()>>;

那么你可以这样做:

auto atexit0 = scope_guard{ []{ /* recycle resources you just allocated */ } };

在你分配一些需要清理的资源之后立即在你的函数体中(而不是 C 风格的“在函数末尾手动执行,可能使用一堆 goto”)。


推荐阅读