【中级】C++析构函数抛出异常

2024-06-21 11:38

本文主要是介绍【中级】C++析构函数抛出异常,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C++标准指出不能在析构函数里面抛出异常!理由呢?

理由一:可能造成资源泄露!

请看如下代码:

#include <stdio.h>class A
{
public:~A(){printf("~A()\n");}
};class SuperBase
{
public:~SuperBase(){printf("~SuperBase()\n");}
};class Base : public SuperBase
{
public:Base(){pi = new int;}~Base(){throw 0;printf("deleting pi.\n");delete pi;}
private:int *pi;A a;
};int main()
{try{Base base;}catch(...){printf("catch block.\n");}return 0;
}

输出是:

~A()
~SuperBase()
catch block.

可见在异常发生时,跟异常对象(base)相关的对象(a,base的SuperBase成分)的析构函数被调用,以释放资源,这如C++标准所说。不过,base对象中pi所指向的动态内存却并未释放,造成了内存泄露。那我们把Base的析构函数改成下面这样,

~Base()
{printf("deleting pi.\n");delete pi;throw 0;
}

这样内存泄露问题不就解决了!这是不是意味着C++标准有问题,因为至少在这里抛出异常没造成什么副作用!看看下面的理由。

理由二:可能造成程序异常退出!
假设Base的析造函数已经改成上面的样子,执行以下代码看看!

int main()
{try{Base base0;Base base1;}catch(...){printf("catch block.\n");}printf("get here.\n");return 0;
}


贴上在g++中的输出:

deleting pi.
~A()
~SuperBase()
deleting pi.
~A()
~SuperBase()
terminate called after throwing an instance of 'int'This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

可见在异常发生时对象相关的资源都被释放了,因为相应的析构函数被调用了,动态分配的内存被释放了!可是异常却没有得到处理!在try块中明明把会抛出异常的代码给包围了,发生异常时却并未进入catch块中!因为并未输出“catch block.”,我们可以断定程序在try块中异常退出!为什么?

因为C++的异常处理模型不能同时处理两次以上的异常!如果两个以上的异常同时抛出,那么程序就调用terminate()函数自杀,这种情况是极难调试的!那么我们try块里面的代码抛出了几次异常?答案是2次。在try块的结尾处,首先会执行base1.Base::~Base(),在其中抛出了异常,此时程序会找匹配的catch块,在这里我们提供了一个,因此可以找到,在进入catch块之前,try块中在异常发生前创建的局部对象必须被销毁,因此又会调用base0.Base::~Base(),在其中又抛出了异常,此时程序中有2个未被处理的异常,因此调用terminate()结束程序。这也就限制了Base对象放到数组,vector,list等容器中,所以要避免在析构函数中抛出异常,如果析构函数中调用的函数可能会抛出异常,可以选择在析构函数内部把这些异常处理掉,像下面这样:

Base::~Base()
{try{call functions throwing exception.}catch(...){}
}



 


 



 


 

这篇关于【中级】C++析构函数抛出异常的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1081122

相关文章

MySQL 中的 CAST 函数详解及常见用法

《MySQL中的CAST函数详解及常见用法》CAST函数是MySQL中用于数据类型转换的重要函数,它允许你将一个值从一种数据类型转换为另一种数据类型,本文给大家介绍MySQL中的CAST... 目录mysql 中的 CAST 函数详解一、基本语法二、支持的数据类型三、常见用法示例1. 字符串转数字2. 数字

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Python内置函数之classmethod函数使用详解

《Python内置函数之classmethod函数使用详解》:本文主要介绍Python内置函数之classmethod函数使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 类方法定义与基本语法2. 类方法 vs 实例方法 vs 静态方法3. 核心特性与用法(1编程客

Python函数作用域示例详解

《Python函数作用域示例详解》本文介绍了Python中的LEGB作用域规则,详细解析了变量查找的四个层级,通过具体代码示例,展示了各层级的变量访问规则和特性,对python函数作用域相关知识感兴趣... 目录一、LEGB 规则二、作用域实例2.1 局部作用域(Local)2.2 闭包作用域(Enclos

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

MySQL count()聚合函数详解

《MySQLcount()聚合函数详解》MySQL中的COUNT()函数,它是SQL中最常用的聚合函数之一,用于计算表中符合特定条件的行数,本文给大家介绍MySQLcount()聚合函数,感兴趣的朋... 目录核心功能语法形式重要特性与行为如何选择使用哪种形式?总结深入剖析一下 mysql 中的 COUNT

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

MySQL 中 ROW_NUMBER() 函数最佳实践

《MySQL中ROW_NUMBER()函数最佳实践》MySQL中ROW_NUMBER()函数,作为窗口函数为每行分配唯一连续序号,区别于RANK()和DENSE_RANK(),特别适合分页、去重... 目录mysql 中 ROW_NUMBER() 函数详解一、基础语法二、核心特点三、典型应用场景1. 数据分