28 July 2023

C++ 核心指南目录

“Use RAII to prevent leaks”

理由

内存泄漏肯定不行。手工管理释放内存资源很容易出错。RAII(Resource Acquisition Is Initialization) 是避免内存泄漏的最简单、最系统化的方式。

例子

void f1(int i)   // Bad: possible leak
{
    int* p = new int[12];
    // ...
    if (i < 17) throw Bad{"in f()", i};
    // ...
}

我们可以在抛出异常之前小心地释放内存资源。

void f2(int i)   // Clumsy and error-prone: explicit release
{
    int* p = new int[12];
    // ...
    if (i < 17) {
        delete[] p;
        throw Bad{"in f()", i};
    }
    // ...
}

但是,这样操作很繁琐。代码量大的时候,在多处可能跑出异常的地方显式地释放资源,既重复是劳动,又容易出错。

void f3(int i)   // OK: resource management done by a handle (but see below)
{
    auto p = make_unique<int[]>(12);
    // ...
    if (i < 17) throw Bad{"in f()", i};
    // ...
}

注意

这个方法在隐式地抛出异常的情况下也能正常工作,因为清理工作在被调用函数中处理掉了。

void f4(int i)   // OK: resource management done by a handle (but see below)
{
    auto p = make_unique<int[]>(12);
    // ...
    helper(i);   // might throw
    // ...
}

除非你需要指针语义,不然的话,就用局部的资源对象:

void f5(int i)   // OK: resource management done by local object
{
    vector<int> v(12);
    // ...
    helper(i);   // might throw
    // ...
}

这样更简单、更安全、更高效率。

注意

如果没有明显的资源句柄,或者很难定义合适的 RAII 对象/句柄,最后的办法是通过某个 final_action 对象清理内存资源。

注意

如果我们写的程序不能执行异常怎么办?首先,我们要去质疑一下,为什么?是不是有一些反异常操作的迷信?我们只知道很少一些情况不能用异常:

  • 我们的系统很小,支持异常操作会吃掉 2K 内存的大部分。
  • 我们使用硬实时系统,没有工具可以确保在规定的时间内处理异常。
  • 我们的系统里有大量旧代码,用了大量难以理解的指针,所以异常会导致内存泄漏。
  • 我们用的 C++ 版本中的异常处理机制性能很糟糕(慢、耗内存、没法和动态链接库兼容)。请像提供商提议,如果没有用户提议,不会有改进发生。
  • 如果我们质疑经理的古老智慧,我们会被炒鱿鱼。

这些理由中,只有第一条是很根本的,所以,只要有可能,尽量通过 RAII 使用异常机制,或者设计你的 RAII 对象,确保永远不会出错。如果没法使用异常机制,就模拟出 RAII 操作。就是在对象创建之后,先检查对象是否有效,然后在析构函数中释放所有资源。其中一个策略是给每一个资源句柄添加一个 valid() 操作。

void f()
{
    vector<string> vs(100);   // not std::vector: valid() added
    if (!vs.valid()) {
        // handle error or exit
    }

    ifstream fs("foo");   // not std::ifstream: valid() added
    if (!fs.valid()) {
        // handle error or exit
    }

    // ...
} // destructors clean up as usual

很明显,这样会增加代码长度,不支持隐式的异常传导,并且也很容易忘记添加 valid() 检查。所以,还是首选使用异常机制。

请查看:使用 noexcept 规则