前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++核心准则​讨论:析构,释放和交换操作必须永不失败

C++核心准则​讨论:析构,释放和交换操作必须永不失败

作者头像
面向对象思考
发布2020-12-15 15:04:10
6300
发布2020-12-15 15:04:10
举报

Discussion: Destructors, deallocation, and swap must never fail

讨论:析构,释放和交换操作必须永不失败

Never allow an error to be reported from a destructor, a resource deallocation function (e.g., operator delete), or a swap function using throw. It is nearly impossible to write useful code if these operations can fail, and even if something does go wrong it nearly never makes any sense to retry. Specifically, types whose destructors might throw an exception are flatly forbidden from use with the C++ Standard Library. Most destructors are now implicitly noexcept by default.

永远不要允许从析构函数,资源释放函数(例如,运算符删除)或交换函数中使用throw报告错误。如果这些操作失败,编写有用的代码几乎是不可能的,发生错误,重试也几乎没有任何意义。特别是,析构函数可能引发异常的类型已经被明确禁止与C ++标准库一起使用。现在默认情况下,大多数析构函数都隐式地为noexcept。

Example(示例)

代码语言:javascript
复制
class Nefarious {
public:
    Nefarious() { /* code that could throw */ }    // ok
    ~Nefarious() { /* code that could throw */ }   // BAD, should not throw
    // ...
};

Nefarious objects are hard to use safely even as local variables:

代码语言:javascript
复制
 void test(string& s)
 {
     Nefarious n;          // trouble brewing
     string copy = s;      // copy the string
 } // destroy copy and then n

在这里,复制s可能会抛出异常,而且抛出异常时,如果n的析构函数也抛出异常,则程序将通过std :: terminate退出,因为两个异常不能同时传播。

Classes with Nefarious members or bases are also hard to use safely, because their destructors must invoke Nefarious' destructor, and are similarly poisoned by its bad behavior:

代码语言:javascript
复制
 class Innocent_bystander {
     Nefarious member;     // oops, poisons the enclosing class's destructor
     // ...
 };

 void test(string& s)
 {
     Innocent_bystander i;  // more trouble brewing
     string copy2 = s;      // copy the string
 } // destroy copy and then i

在这里,如果copy2的构造过程抛出异常,我们将遇到相同的问题,因为我的析构函数现在也可能抛出异常,如果是,std :: terminate将会被触发。

You can't reliably create global or static Nefarious objects either:

代码语言:javascript
复制
 static Nefarious n;       // oops, any destructor exception can't be caught

You can't reliably create arrays of Nefarious:

代码语言:javascript
复制
 void test()
 {
     std::array<Nefarious, 10> arr; // this line can std::terminate(!)
 }

如果存在引发异常的析构函数,数组的行为是不确定的,因为没有合理的回滚行为可以设计。试想一下:编译器可以生成什么代码来构造arr,如果第四个对象的构造函数抛出该代码,则该代码必须放弃,并在其清理模式下尝试调用已构造对象的析构函数...这些更多的析构函数会抛出异常么?没有令人满意的答案。

You can't use Nefarious objects in standard containers:

代码语言:javascript
复制
 std::vector<Nefarious> vec(10);   // this line can std::terminate()

标准库禁止所有与其一起使用的析构函数抛出异常。您不能将Nefarious对象存储在标准容器中,也不能将它们与标准库的任何其他部分一起使用。

Note(注意)

These are key functions that must not fail because they are necessary for the two key operations in transactional programming: to back out work if problems are encountered during processing, and to commit work if no problems occur. If there's no way to safely back out using no-fail operations, then no-fail rollback is impossible to implement. If there's no way to safely commit state changes using a no-fail operation (notably, but not limited to, swap), then no-fail commit is impossible to implement.

这些是必不可少的关键功能,因为它们是事务编程中两个关键操作所必需的:如果在处理过程中遇到问题,则回滚工作;如果没有问题,则提交工作。如果无法使用无失败操作安全地退出,则无失败回滚是不可能实现的。如果无法使用无失败操作(特别是但不限于交换)来安全地提交状态更改,那么就不可能实现无失败提交。

Consider the following advice and requirements found in the C++ Standard:

考虑C ++标准中的以下建议和要求:

If a destructor called during stack unwinding exits with an exception, terminate is called (15.5.1). So destructors should generally catch exceptions and not let them propagate out of the destructor. --[C++03] §15.2(3)

如果在堆栈展开期间调用的析构函数异常退出,则将终止(15.5.1)。因此,析构函数通常应捕获异常,而不应让它们传播出析构函数。-[C ++ 03]§15.2(3)

No destructor operation defined in the C++ Standard Library (including the destructor of any type that is used to instantiate a standard-library template) will throw an exception. --[C++03] §17.4.4.8(3)

C ++标准库中定义的析构函数操作(包括用于实例化标准库模板的任何类型的析构函数)都不会引发异常。-[C ++ 03]§17.4.4.8(3)

Deallocation functions, including specifically overloaded operator delete and operator delete[], fall into the same category, because they too are used during cleanup in general, and during exception handling in particular, to back out of partial work that needs to be undone. Besides destructors and deallocation functions, common error-safety techniques rely also on swap operations never failing -- in this case, not because they are used to implement a guaranteed rollback, but because they are used to implement a guaranteed commit. For example, here is an idiomatic implementation of operator= for a type T that performs copy construction followed by a call to a no-fail swap:

取消分配功能(特别包括重载的运算符delete和operator delete [])属于同一类,因为它们通常在清理过程中(尤其是在异常处理过程中)也用于撤消需要撤消的部分工作。除了析构函数和释放函数之外,常见的安全的错误处理技术还依赖于永不失败的交换操作-在这种情况下,不是因为它们用于实现有保证的回滚,而是因为它们用于实现有保证的提交。例如,以下是对类型T的operator =的惯用实现,该类型T执行拷贝构造,然后调用无失败交换:

代码语言:javascript
复制
T& T::operator=(const T& other)
{
    auto temp = other;
    swap(temp);
    return *this;
}

(See also Item 56. ???)

(另请参阅第56项。)

Fortunately, when releasing a resource, the scope for failure is definitely smaller. If using exceptions as the error reporting mechanism, make sure such functions handle all exceptions and other errors that their internal processing might generate. (For exceptions, simply wrap everything sensitive that your destructor does in a try/catch(...) block.) This is particularly important because a destructor might be called in a crisis situation, such as failure to allocate a system resource (e.g., memory, files, locks, ports, windows, or other system objects).

幸运的是,释放资源时,失败的范围肯定较小。如果使用异常作为错误报告机制,请确保此类函数处理其内部处理可能生成的所有异常和其他错误。(对于例外情况,只需将您的析构函数所做的所有敏感操作都包装在try / catch(...)块中。)这尤其重要,因为在危机情况下可能会调用析构函数,例如无法分配系统资源(例如,,内存,文件,锁,端口,窗口或其他系统对象)。

When using exceptions as your error handling mechanism, always document this behavior by declaring these functions noexcept. (See Item 75.)

当使用异常作为错误处理机制时,请始终通过声明这些函数noexcept来说明此类行为。(请参阅第75条。)

原文链接https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#discussion-destructors-deallocation-and-swap-must-never-fail

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 面向对象思考 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Discussion: Destructors, deallocation, and swap must never fail
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档