Effective C++ 学习笔记 条款08 别让异常逃离析构函数

C++并不禁止析构函数吐出异常,但它不鼓励你这样做。这是有理由的,考虑以下代码:

class Widget
{
public:// ...~Widget() { /* ... */ }    // 假设这个可能吐出一个异常
};void doSomething()
{std::vector<Widget> v;// ...
}    // v在这里被销毁

当vector v被销毁时,它有责任销毁其内含的所有Widgets。假设v内含十个Widges,而在析构第一个元素期间,有个异常被抛出。其他九个Widgets还是应该被销毁(否则它们保存的任何资源都会发生泄漏),因此v应该调用它们各个析构函数。但假设在那些调用期间,第二个Widget析构函数又抛出异常。现在有两个同时作用的异常,这对C++而言太多了。在两个异常同时存在的情况下,程序不是结束执行就是导致不明确的行为。本例它会导致不明确的行为。使用标准程序库的任何其它容器(如list、set)或TR1的任何容器(见条款54)或甚至array,也会出现相同情况。容器或array并非遇上麻烦的必要条件,只要析构函数吐出异常,即使并非使用容器或arrays,程序也可能过早结束或出现不明确行为。

这很容易理解,但如果你的析构函数必须执行一个动作,而该动作可能会在失败时抛出异常,该怎么办?举个例子,假设你使用一个class负责数据库连接:

class DBConnection
{
public:// ...static DBConnection create();    // 这个函数返回DBConnection对象,为了简化省略参数void close();    // 关闭联机:失败则抛异常
};

为确保客户不忘记在DBConnection对象身上调用close(),一个合理的想法是创建一个用来管理DBConnection资源的class,并在其析构函数中调用close。这一类用于资源管理的classes在第3章有详细探讨,这儿只要考虑它们的析构函数长相就够了:

class DBConn    // 这个class用来管理DBConnection对象
{
public:// ...~DBConn()    // 确保数据库连接总是会被关闭{db.close();}
private:DBConnection db;
};

以便允许客户写出这样的代码:

{    // 开启一个区块(block)DBConn dbc(DBConnection::create());    // 建立DBConnection对象并交给DBConn对象以便管理// ... 通过DBConn的接口使用DBConnection对象
}    // 在区块结束点,DBConn对象被销毁,因而自动为DBConnection对象调用close

只要调用close成功,一切都美好。但如果该调用导致异常,DBConn析构函数会传播该异常,也就是允许它离开这个析构函数,就会造成问题。

两个办法可以避免这一问题。DBConn的析构函数可以:
1.如果close抛出异常就结束程序。通常通过调用abort完成:

DBConn::~DBConn()
{try{db.close();}catch (...){记录log;std::abort();}
}

如果程序遭遇一个“于析构期间发生的错误”后无法继续执行,“强迫结束程序”是个合理选项。毕竟它可以阻止异常从析构函数传播出去(那会导致不明确的行为)。即调用abort()抢先置“不明确行为”于死地。

2.吞下因调用close而发生的异常:

DBConn::~DBConn()
{try{db.close();}catch (...){记录log;}
}

一般而言,将异常吞掉是个坏主意,因为它压制了“某些动作失败”的重要信息!然而有时候吞下异常也比负担“草率结束程序”或“不明确行为带来的风险”好。为了让这成为一个可行方案,程序必须能够继续可靠地执行,即使在刚遭遇并忽略一个错误之后。

这些办法都没什么吸引力。问题在于两者都无法对“导致close抛出异常”的情况做出反应。

一个较佳策略是重新设计DBConn接口,使其客户有机会对可能出现的问题作出反应。例如DBConn自己可以提供一个close函数,因而赋予客户一个机会得以处理“因该操作而发生的异常”。DBConn也可以追踪其所管理的DBConnection是否已被关闭,并在答案为否的情况下关闭之。然而如果DBConnection析构函数调用close失败,我们又将回到“强迫结束程序”或“吞下异常”的老路:

class DBConn {
public:// ...void close()    // 供客户使用的新函数{db.close();closed = true;}~DBConn(){if (!closed){try{    // 关闭连接,如果客户没有关db.close();}catch (...){记录log;    // 如果关闭动作失败,记录下来并结束游戏或吞下异常// ...}}}private:DBConnection db;bool closed;
};

把调用close的责任从DBConn析构函数手上移到DBConn客户手上(但DBConn析构函数仍内含一个“双保险”调用)可能会给你“肆无忌惮转移负担”的印象。你甚至可能认为它违反条款18所提忠告(让接口容易被正确使用)。实际上这两项污名不成立。如果某个操作可能在失败时抛出异常,而又存在某种需要必须处理该异常,那么这个衣长必须来自析构函数以外的某个函数。因为析构函数吐出异常就是危险,总会带来“过早结束程序”或“发生不明确行为”的风险。本例要说的是,由客户自己调用close并不会对他们带来负担,而是给他们一个处理错误的机会,否则他们没机会响应。如果他们不认为这个机会有用(或许他们坚信不会有错误发生),可以忽略它,倚赖DBConn析构函数去掉用close。如果真有错误发生——如果close的确抛出异常——而且DBConn吞下该异常或结束程序,客户没有立场抱怨,毕竟他们曾有机会第一手处理问题,而他们选择了放弃。

请记住:
1.析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕获任何异常,然后吞下它们(不传播)或结束程序。

2.如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/720001.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【网站项目】139选课排课系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Redis 群集

简介 在现在的互联网大潮中&#xff0c;NoSQL可谓家喻户晓&#xff0c;Redis作为NoSOL大军中极其重要的一员&#xff0c;是我们走向架构道路的一条必经之路。 Redis介绍 Redis 数据库是一个非关系型数据库&#xff0c;在正式学习Redis之前&#xff0c;我们先来了解关系型数据库…

算法沉淀——动态规划之完全背包问题(leetcode真题剖析)

算法沉淀——动态规划之完全背包问题 01.【模板】完全背包02.零钱兑换03.零钱兑换 II04.完全平方数 完全背包问题是背包问题的一种变体&#xff0c;与01背包问题不同&#xff0c;它允许你对每种物品进行多次选择。具体来说&#xff0c;给定一个固定容量的背包&#xff0c;一组物…

TCPDump 使用教程

每次服务器网络不通的时候&#xff0c;总会听到一个声音&#xff0c;你去抓包啊&#xff0c;那这里就来介绍下TCPDump&#xff0c;一款强大的网络分析工具&#xff0c;可以捕获网络上的数据包&#xff0c;并进行分析。这款工具在网络管理员和安全专家中非常受欢迎。 一、安装 …

防火墙:网络防御的第一道防线

目录 引言 一、安全技术与防火墙 &#xff08;一&#xff09;安全技术 &#xff08;二&#xff09;防火墙的主要功能与分类 1.防火墙的主要功能 2.防火墙的分类 二、Linux防火墙的基本认识 &#xff08;一&#xff09;Netfilter &#xff08;二&#xff09;防火墙工具…

单调队列(347. 前 K 个高频元素239. 滑动窗口最大值)

单调队列和单调栈其实差不多,就是维护一个区间单调的队列或者是栈,单调队列就是我们所说的大顶堆小顶堆, //升序队列 小顶堆 great 小到大 priority_queue <int,vector<int>,greater<int> > pri_que; //降序队列 大顶堆 less 大到小 默认 priority_qu…

【AI视野·今日NLP 自然语言处理论文速览 第八十一期】Mon, 4 Mar 2024

AI视野今日CS.NLP 自然语言处理论文速览 Mon, 4 Mar 2024 Totally 48 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Mitigating Reversal Curse via Semantic-aware Permutation Training Authors Qingyan Guo, Rui Wang, Junlia…

Fisher矩阵与自然梯度法

文章目录 Fisher矩阵及自然梯度法Fisher矩阵自然梯度法总结参考链接 Fisher矩阵及自然梯度法 自然梯度法相比传统的梯度下降法具有以下优势&#xff1a; 更好的适应性&#xff1a;自然梯度法通过引入黎曼流形上的梯度概念&#xff0c;能够更好地适应参数空间的几何结构。这使…

今日学习总结2024.3.3

今天也是全天自习的一天&#xff0c;非常充实 早上八点开始接着晚上的docker配置&#xff0c;并一边同步博客&#xff0c;还是遇到了卡壳看了一个视频&#xff0c;里面提到了物联网&#xff0c;感觉对这个概念更加了解了&#xff0c;当年填报专业志愿的时候有将物联网工程放在…

LCR 134. Pow(x, n)

解题思路&#xff1a; 分治 快速幂 Java中向下取整n/2即可 需要结合下图理解&#xff0c;算法就是实现的该过程 class Solution {public double myPow(double x, int n) {if(x 0.0f) return 0.0d;long b n;double res 1.0;//例如:2^-5(1/2)^5if(b < 0) {x 1 / x;b -b…

Python中可迭代数据类型

有序集合 有序的集合包括&#xff1a;列表、字符串以及元组。 对于任意Python序列可应用的运算&#xff1a; 运算名运算符解释长度len(sequence)返回序列的长度。索引访问sequence[i]返回索引 i 处的元素。切片sequence[i:j]返回从索引 i 到 j 的子序列。成员检查item in seq…

访问⾸⻚的速度很慢,有哪些⽅法可以提⾼访问速度?

1. 减少HTTP请求 可以通过简化页面设计&#xff0c;减少页面中图片、样式表、JavaScript等组件的数量来降低HTTP请求次数。此外&#xff0c;合并文件也是一个有效的方法&#xff0c;即将所有脚本或样式表文件合并为一个文件&#xff0c;和使用图片精灵以减少请求的数量。 2. …

【Python】Python教师/学生信息管理系统 [简易版] (源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

accept()函数

accept 函数是用于在服务器端接受客户端连接的系统调用。以下是 accept 函数的详细解读&#xff1a; #include <sys/types.h> #include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);sockfd&#xff1a; 是一个已经通过 soc…

京东商品优惠券API获取商品到手价

item_get_app-获得JD商品详情原数据 公共参数 请求地址: jd/item_get_app 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,i…

MATLAB环境下基于区域椭圆拟合的细胞分割方法

使用图像分割技术可以找到图像中的目标区域&#xff0c;目标区域可以定义为具有特定值的单个区域&#xff0c;也可以定义为具有相同值的多个区域。目前图像分割已经融入到生活中的方方面面&#xff0c;在遥感领域&#xff0c;它应用于航拍图中的地形、地貌的分割&#xff1b;在…

高可用架构实现流量治理的策略

一 概述 1.1 高可用评判指标 1.平均故障间隔&#xff08;Mean Time Between Failure&#xff0c;简称 MTBF&#xff09;&#xff1a;表示两次故障的间隔时间&#xff0c;也就是系统正常运行的平均时间&#xff0c;这个时间越长&#xff0c;说明系统的稳定性越高&#xff1b; …

[AIGC] Flink入门教程:理解DataStream API(Java版)

简介 Apache Flink是一款开源的流处理框架&#xff0c;它在大数据处理场景中被广泛应用。Flink的数据流API&#xff08;DataStream API&#xff09;是一个强大的、状态匹配的流处理API&#xff0c;它可以处理有界和无界数据流。 本教程将向你介绍如何使用Java来编写使用DataS…

SpringMVC框架②

三、RequestMapping注解 3、RequestMapping注解的value属性 必须设置 发送一个请求最直观的表示方式就是一个请求路径 altenter 进入接口方法 再用 alte7 查看里面的属性 value值可以是数组 value{"test","test1"} 只满足任何一个请求地址就会调用此方…

GO语言学习笔记(与Java的比较学习)(五)

Map 概念 map 是引用类型&#xff0c;可以使用如下声明&#xff1a; var map1 map[keytype]valuetype var map1 map[string]int 在声明的时候不需要知道 map 的长度&#xff0c;map 是可以动态增长的。 未初始化的 map 的值是 nil&#xff08;即零值为nil&#xff09;&…