专栏首页C++核心准则原文翻译C++核心准则E.6:使用RAII防止资源泄露

C++核心准则E.6:使用RAII防止资源泄露

E.6: Use RAII to prevent leaks

E.6:使用RAII防止资源泄露

Reason(原因)

Leaks are typically unacceptable. Manual resource release is error-prone. RAII ("Resource Acquisition Is Initialization") is the simplest, most systematic way of preventing leaks.

资源泄露通常都是不可接受的。手动释放资源容易引发错误。RAII(“资源请求即初始化”)是防止泄露最简单,更加系统化的方式。

Example(示例)

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

We could carefully release the resource before the throw:

在抛出异常之前,我们必须小心地释放资源:

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};
    }
    // ...
}

This is verbose. In larger code with multiple possible throws explicit releases become repetitive and error-prone.

代码冗长。在更大规模的,存在更多的抛出异常的可能性的代码中,显示释放资源会更加繁复和易错。

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};
    // ...
}

Note that this works even when the throw is implicit because it happened in a called function:

需要注意的是,即使是隐式的抛出动作(因为它在调用的函数中发生),这段代码仍然可以起作用。

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

Unless you really need pointer semantics, use a local resource object:

除非你真的需要指针语义,否则使用一个局部的资源对象:

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

That's even simpler and safer, and often more efficient.

这样更简单,更安全,甚至更高效。

Note(注意)

If there is no obvious resource handle and for some reason defining a proper RAII object/handle is infeasible, as a last resort, cleanup actions can be represented by a final_action object.

如果没有明显的资源句柄而且由于某种原因定义适当的RAII对象/句柄不可行,作为最有手段,可以通过一个final_actrion对象实现清理动作。

Note(注意)

But what do we do if we are writing a program where exceptions cannot be used? First challenge that assumption; there are many anti-exceptions myths around. We know of only a few good reasons:

但是,如果我们在写一个程序而无法使用异常处理时,我们应该做什么?首先挑战这个假设;存在很多反对使用异常的神话。我们只知道很少几个是正当理由:

  • We are on a system so small that the exception support would eat up most of our 2K memory.
  • 你正在工作的系统是如此之小,支持异常会吃掉最多2K内存(都无法承受)。
  • We are in a hard-real-time system and we don't have tools that guarantee us that an exception is handled within the required time.
  • 我们处在一个硬实时系统中,没有工具可以保证异常处理会在要求的时间内完成。
  • We are in a system with tons of legacy code using lots of pointers in difficult-to-understand ways (in particular without a recognizable ownership strategy) so that exceptions could cause leaks.
  • 我们所处的系统包含成吨的遗留代码,这些代码以难以理解的方式大量使用指针(通常没有可识别的所有权策略),因此异常可能引发泄露。
  • Our implementation of the C++ exception mechanisms is unreasonably poor (slow, memory consuming, failing to work correctly for dynamically linked libraries, etc.). Complain to your implementation purveyor; if no user complains, no improvement will happen.
  • 正在使用的C++实现,其异常机制超乎想象的差劲(缓慢,过多消费内存,使用动态链接库时无法工作等)。投诉你的提供者;如果没有用户投诉,就不会发生改进。
  • We get fired if we challenge our manager's ancient wisdom.
  • 如果我们挑战管理者的老套经验,就会被开除。

Only the first of these reasons is fundamental, so whenever possible, use exceptions to implement RAII, or design your RAII objects to never fail. When exceptions cannot be used, simulate RAII. That is, systematically check that objects are valid after construction and still release all resources in the destructor. One strategy is to add a valid() operation to every resource handle:

这些原因中只有第一个是根本性的,因此只要可能就使用异常来实现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

Obviously, this increases the size of the code, doesn't allow for implicit propagation of "exceptions" (valid() checks), and valid() checks can be forgotten. Prefer to use exceptions.

很显然,这回增加代码的大小,不允异常的隐式传播(通过有效性检查),而且有效性检查可以被忘记。最好使用异常。

See also: Use of noexcept

参见:使用noexcept。

Enforcement(实时建议)

原文链接https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#e6-use-raii-to-prevent-leaks

本文分享自微信公众号 - 面向对象思考(OOThinkingDalian),作者:面向对象思考

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-07-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C++核心准则Enum.7: 只在必要时定义枚举的底层类型

    The default is the easiest to read and write. int is the default integer type. i...

    面向对象思考
  • C++核心准则E.25:如果不能抛出异常,模仿RAII方式进行资源管理

    Even without exceptions, RAII is usually the best and most systematic way of dea...

    面向对象思考
  • C++核心准则CP.26:不要分离线程

    Often, the need to outlive the scope of its creation is inherent in the threads ...

    面向对象思考
  • cmake:vs2015/MinGW静态编译leveldb

    leveldb是google的开源项目(https://github.com/google/leveldb), 在linux下编译很方便,然而官方版本却没有提供...

    用户1148648
  • OpenSSL与yaSSL性能对比

    摘要 MySQL可以使用OpenSSL或yaSSL/wolfSSL进行编译,这两者都支持基于OpenSSL API的加密连接。在5.7版本,我们知道默认情况下...

    腾讯数据库技术
  • Cocos Creator中使用动作系统(官方文档摘录)

    Cocos Creator 提供的动作系统源自 Cocos2d-x,API 和使用方法均一脉相承。动作系统可以在一定时间内对节点完成位移,缩放,旋转等各种动作。

    bering
  • 第16次文章:Java字节码

    在上一期讲解java的动态性的时候,我们主要提到了java中的反射机制,可以在java代码运行的时候,改变类的结构,属性等信息,而这一节我们通过另一种实现方式来...

    鹏-程-万-里
  • Effective.Java 读书笔记(10)关于toString

    针对于java.lang.Object已经帮我们实现好了的toString方法,当我们自己定义出来的类使用这古老的toString方法的时候,通常不会返回给你一...

    Mezereon
  • 终极一战:为了编程面试!

    过去常常读一个问题,然后花几分钟把它映射到我以前见过的类似问题上。如果我可以映射它,我将只关注这个问题与父问题相比有哪些不同约束。如果这是一个新问题,那么我会尝...

    量化投资与机器学习微信公众号
  • 勇士与公主(组合+完全背包)- HDU 1028

    这是一道排列组合题,然而却用到了完全背包的思想,寻找真相并不是件容易的事情,先简单看下题目,慢慢进入正题

    ACM算法日常

扫码关注云+社区

领取腾讯云代金券