cpp_12_异常处理

1  异常理论

1.1  何为异常?

        在实际运行环境中发生,却在设计、编码、测试阶段无法预料的,各种潜在的问题。

1.2  报告异常的2种机制

        1)通过 return 返回值报告异常信息:

                所有局部对象都能正确地被析构、被释放

                定位错误点,需要逐层判断,流程繁琐

        2)抛出--捕获异常对象:

                所有局部对象都能正确地被析构、被释放

                定位错误点,可一步到位,流程简单

// return.cpp  1)通过return报告异常信息
#include <iostream>
#include <cstdio>
using namespace std;class A {
public:A() {   cout << "A() is invoked" << endl;   }~A() {  cout << "~A() is invoked" << endl;  }
};// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int foo() {cout << "foo出错前的几百行代码" << endl;A a;FILE* pfile = fopen("./cfg", "r");if( !pfile ) return -1; // (1)将数据返回给调用者 (2)跳转至右花括号cout << "foo出错后的几百行代码" << endl;return 0;
} // a.~A()  释放a本身所占内存空间int bar() {cout << "bar出错前的几百行代码" << endl;A b;if( foo()==-1 )return -1;cout << "bar出错后的几百行代码" << endl;return 0;
} // b.~A()  释放b本身所占内存空间int hum( ) {cout << "hum出错前的几百行代码" << endl;A c;if( bar()==-1 )return -1;cout << "hum出错后的几百行代码" << endl;return 0;
} // c.~A()  释放c本身所占内存空间int main( void ) {cout << "main出错前的几百行代码" << endl;A d;if( hum()==-1 )return -1;cout << "main出错后的几百行代码" << endl;return 0;
} // d.~A()  释放d本身所占内存空间

2  抛出异常(throw异常对象)

        1)可以抛出基本类型的对象,如:

                throw  -1;

                throw  "内存分配失败";

        2)可以抛出类类型的对象,如:

                MemoryException  ex;

                throw  ex;  // 栈对象

                throw  MemoryException();  // 匿名堆对象

        3)但不要抛出局部对象的指针,如:

                MemoryException  ex;

                throw  &ex;  // 错误!(同C中,不要返回局部变量的地址)

3  捕获异常(try  catch)

        try { 可能引发异常的语句; }

        catch ( 异常类型1&  ex ) { 针对异常类型1的异常处理; }

        catch ( 异常类型2&  ex ) { 针对异常类型2的异常处理; }

        ...

        catch ( 异常类型n&  ex ) { 针对异常类型n的异常处理; }

   

// throwPre.cpp 利用throw报告异常信息
//              对于能解决的异常,尽早捕获;
//              对于不能解决的异常,远点捕获,使程序尽早结束。
#include <iostream>
#include <cstdio>
using namespace std;class A {
public:A() {   cout << "A() is invoked" << endl;   }~A() {  cout << "~A() is invoked" << endl;  }
};// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
void foo() {cout << "foo出错前的几百行代码" << endl;A a;FILE* pfile = fopen("./cfg", "r");if( !pfile ) throw -1; cout << "foo出错后的几百行代码" << endl;
} // a.~A()  释放a本身所占内存空间void bar() {cout << "bar出错前的几百行代码" << endl;A b;
//    try {foo();
//    }
//    catch( int e ) {
//        cout << "bar函数中捕获异常信息: " << e << endl;
//    }cout << "bar出错后的几百行代码" << endl;
} // b.~A()  释放b本身所占内存空间void hum( ) {cout << "hum出错前的几百行代码" << endl;A c;
//    try {bar();
//    }
//    catch( int e ) {
//        cout << "hum函数中捕获异常信息: " << e << endl;
//    }cout << "hum出错后的几百行代码" << endl;
} // c.~A()  释放c本身所占内存空间int main( void ) {cout << "main出错前的几百行代码" << endl;A d;try {hum();}catch( int e ) {cout << "main函数中捕获异常信息: " << e << endl;}cout << "main出错后的几百行代码" << endl;return 0;
} // d.~A()  释放d本身所占内存空间

 

4  建议、要求、总结

        1)建议:在catch子句中使用引用接收异常对象,减少一次克隆。

             建议:以匿名临时对象的形式抛出异常,编译器优化会减少一次克隆。

        2)对于能解决的异常,尽早捕获;对于不能解决的异常,远点捕获,使程序尽早结束。

        3)要求:异常对象必须允许被拷贝构造和析构

        4)匹配顺序:根据异常对象的类型,自上至下顺序匹配,而非最优匹配 

              因此对子类类型异常的捕获,要放在对基类类型异常的捕获之前。

        5)可以抛出基本类型类类型的异常,建议抛类类型的异常,携带更多诊断信息,以便查错

        6)可在catch中继续抛出所捕获的异常,或其他异常

        7)任何未被捕获的异常,默认的处理方式就是中止进程 

        8)忽略异常:捕获异常,但不做处理:  catch (...){}   //三个点

// 关于 异常处理的 几点建议
#include <iostream>
#include <cstdio>
using namespace std;class A {
public:A() {}A( const A& that ) { cout << "A类的拷贝构造函数被调用" << endl; }~A() {}
};// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
void foo( ) {A a;throw A(); //建议抛出的为 匿名临时对象(编译器将优化,减少1次克隆)
}
int main( void ) {try {foo();}catch( A& e ) { // 建议利用引用 接收异常对象,减少1次克隆// ...}return 0;
}
// throw2.cpp 建议将 子类类型异常捕获 放在 基类类型异常捕获 之前
//                  (顺序匹配,而非最优匹配)
#include <iostream>
#include <cstdio>
using namespace std;class A {};
class B : public A {};// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
void foo( ) {throw B();
}
int main( void ) {try {foo();}catch( B& e ) { // 对子类类型异常的捕获cout << "B类型的catch捕获" << endl;}catch( A& e ) { // 对基类类型异常的捕获cout << "A类型的catch捕获" << endl;}return 0;
}

5  "异常说明"

        1)异常说明是函数原型的一部分,旨在说明函数可能抛出的异常类型:

                返回类型  函数名 (形参表) throw (异常类型1,异常类型2,...) { 函数体; }

        2)异常说明是一种承诺,承诺函数不会抛出 异常说明以外的异常类型:

                如果抛出了异常说明以外的异常类型,该异常无法被捕获,(而被编译器捕获),进程

                中止

        3)隐式抛出异常的函数,也可以列出它的异常说明。

// except.cpp
// 异常说明 - 给 调用者 说明, 函数内部可能会抛出 哪些类型的异常
//         - 给 调用者 承诺, 函数内部绝对不会抛出异常说明以外异常类型
#include <iostream>
#include <cstdio>
using namespace std;void foo() throw(int,double, const char*) { // 显式抛出异常的函数throw "hello world"; // 3.14; // -1;
}void bar() throw(int,double, const char*) { // 隐式抛出异常的函数foo();// 调用几十个不同函数,而且每个函数都可能抛出异常,并且抛出的异常类型都不相同
}// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {try {bar();
//        foo();    }catch(int& e) {cout << "1. 捕获异常信息:" << e << endl;}catch(double& e) {cout << "2. 捕获异常信息:" << e << endl;}catch(const char* e) {cout << "3. 捕获异常信息:" << e << endl;}return 0;
}

        4)异常说明可以没有,也可以为空

                没有,表示可能抛出任何类型的异常   void foo (void)             { ... }

                为空,表示不会抛出任何类型的异常   void foo (void) throw() { ... }

        5)异常说明在函数的声明定义中必须保持严格一致,否则编译报错。

// except2.cpp
// 没有异常说明 - 给 调用者 承诺,函数内部可能抛出任何类型的异常
// 异常说明为空 - 给 调用者 承诺,函数内部不抛出任何类型的异常
#include <iostream>
#include <cstdio>
using namespace std;void foo() {throw "hello world"; // 3.14; // -1;
}void bar() throw() {throw -1;
}void hum() throw(int,double); // 声明void hum() throw(int,double) { // 定义}// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {try {
//        bar();foo();    }catch(...) { // 捕获异常,但不处理}/*catch(int& e) {cout << "1. 捕获异常信息:" << e << endl;}catch(double& e) {cout << "2. 捕获异常信息:" << e << endl;}catch(const char* e) {cout << "3. 捕获异常信息:" << e << endl;}*/return 0;
}

6  构造函数中的异常

        1)构造函数可以抛出异常,某些时候还必须抛出异常

            - 构造过程中可能遇到各种错误,比如内存分配失败

            - 构造函数没有返回值,无法通过返回值通知调用者

        2)构造函数抛出异常,对象将被不完整构造,这个对象的析构函数永不执行

            - 所有对象形式的成员变量,在抛出异常的瞬间,能得到正确地析构(构造函数的回滚机制)

            - 所有动态分配的资源,必须在抛出异常之前,自己手动释放,否则形成资源的泄露

// consExcept.cpp 构造函数中的异常
#include <iostream>
#include <cstdio>
using namespace std;class A {
public:A() { cout << "A() is invoked" << endl; }~A() { cout << "~A() is invoked" << endl; }
};
class C {
public:C() : m_p(new int) {//【A m_a;】定义m_a,利用m_a.A()//【int* m_p=new int;】定义m_p,初值为指向一块堆内存(动态资源)cout << "C() is invoked" << endl;FILE* pfile = fopen("./file", "r");if( !pfile ) {delete m_p; // 动态资源需要自己手动释放// 对于m_a,利用 m_a.~A()         //回滚机制// 释放 m_a/m_p 本身所占内存空间  //回滚机制throw -1;}// ... 构造函数后续代码...}~C() {delete m_p;cout << "~C() is invoked" << endl;// 对于m_a,利用 m_a.~A()// 释放 m_a/m_p 本身所占内存空间}
private:A m_a;int* m_p;
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {try {C c; // 定义c,利用c.C()} // 如果c是完整构造的对象,将利用c.~C() , 但是如果c是残缺对象将不会调用~C()catch( ... ) {}return 0;
}

7  析构函数中的异常

        1)析构函数中主动抛出异常

              在两种情况下,析构函数会被调用

                        一:正常销毁对象:离开作用域或显式delete

                        二:在异常传递的堆栈辗转开解(stack-unwinding)过程中

              对于第二种情况,异常正处于激活状态,而析构函数也抛出了异常,即同时有两个

              异常,这时C++将通过std::terminate()函数,令进程中止。这是不希望发生的~

        2)对于可能引发异常的操作(被动抛异常),尽量在析构函数内部消化

                                                              try {...} catch (...) { ... }

// desconExcept.cpp 不 在析构函数中 "主动" 抛出异常
#include <iostream>
#include <cstdio>
using namespace std;void foo() {throw "foo函数中抛出的异常";
}void bar() {throw "bar函数中抛出的异常";
}class A {
public:~A() { try {bar();}catch( const char* e ) {cout << "析构函数中捕获异常信息: " << e << endl;}
//        throw "析构函数中抛出的异常";}
};
// 模拟类的设计者(类库、别人设计的类、自己设计的类)
// --------------------------------
// 模拟用户(使用类的人)
int main( void ) {try {A a;foo();//...后续代码...} // a.~A()  ...catch( const char* e ) {cout << "main中捕获异常信息: " << e << endl;}return 0;
}

8  标准库异常类

        建议抛以下类的异常:

        

// exceptPractice 捕获异常练习
#include <iostream>
#include <stdexcept> //std::bad_alloc
#include <typeinfo>  //std::bad_cast
using namespace std;/*
int main( void ) {try {new int[0xFFFFFFFFF];    }catch( bad_alloc& e ) {cout << "new 操作符申请内存失败" << endl;}
}
*/class A {
public:virtual void foo() {}
};
class B : public A {};
class C : public B {};int main( void ) {B b;A& a = b;try {dynamic_cast<C&>(a); // 如果成功 C类型的引用 引用了 B类对象 (有风险)}catch( bad_cast& e ) {cout << "动态类型转换失败" << endl;}
}

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

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

相关文章

中仕教育:公务员政审需要哪些材料?

公务员政审则是公务员招聘过程中的重要环节&#xff0c;政审需要哪些材料呢? 一、个人基本信息 1. 身份证、户口本原件及复印件; 2. 学历学位证书原件及复印件; 3. 近期免冠一寸彩色照片2张; 4. 无犯罪记录证明材料; 5. 个人自传 6. 提供本人的现实表现证明材料(应届生…

【数据结构初阶】——顺序表

本文由睡觉待开机原创&#xff0c;转载请注明出处。 本内容在csdn网站首发 欢迎各位点赞—评论—收藏 如果存在不足之处请评论留言&#xff0c;共同进步&#xff01; 这里写目录标题 1.数据结构2.顺序表线性表顺序表的结构 3.动态顺序表的实现 1.数据结构 数据结构的概念&…

UKP3d的管道编辑

山西这家用户在使用UKP3d时&#xff0c;提出以下问题&#xff1a; 1、stp导入的模型怎么测量距离&#xff1b;另外需要把某一个点移动至原点坐标&#xff0c;这个怎么操作呢&#xff1f; 回复&#xff1a;dist&#xff08;主要是捕捉点&#xff0c;推荐使用&#xff08;开启精…

Java面试汇总——jvm篇

目录 JVM的组成&#xff1a; 1、JVM 概述(⭐⭐⭐⭐) 1.1 JVM是什么&#xff1f; 1.2 JVM由哪些部分组成&#xff0c;运行流程是什么&#xff1f; 2、什么是程序计数器&#xff1f;(⭐⭐⭐⭐) 3、介绍一下Java的堆(⭐⭐⭐⭐) 4、虚拟机栈(⭐⭐⭐⭐) 4.1 什么是虚拟机栈&…

Spark面试题

1. spark core 1.简述hadoop 和 spark的不同点&#xff08;为什么spark更快&#xff09;♥♥♥ shuffle都是需要落盘的&#xff0c;因为在宽依赖中需要将上一个阶段的所有分区数据都准备好&#xff0c;才能进入下一个阶段&#xff0c;那么如果一直将数据放在内存中&#xff0c…

【iOS】UICollectionView的基本使用

使用UITableView作为表格来展示数据完全没有问题&#xff0c;但仍有许多局限性&#xff0c;对于一些更加复杂的布局样式&#xff0c;就有些力不从心了 比如&#xff0c;UITableView只允许表格每一行只能显示一个cell&#xff0c;而不能在一行中显示多个cell&#xff0c;对于这…

数据结构_C++语言描述_高教出版社

contents 前言一、绪论1.1 数据分析结构存储算法计算1.1.1 逻辑结构1.1.2 存储结构1.1.3 算法实现 1.2 数据类型1.3 算法方法 二、线性表2.1 线性表的逻辑结构2.2 线性表的存储结构2.2.1 顺序存储结构2.2.2 链式存储结构 2.3 线性表的操作算法2.3.1 顺序表的操作算法2.3.2 链表…

【c++函数重载】

文章目录 一. 命名空间二 .全缺省参数和半缺省参数三 . 函数重载 一. 命名空间 1.不指定域&#xff1a;先在局部找&#xff0c;再全局。 2. 指定域&#xff1a;到指定的命名空间去找。 3. 当把指定命名空间放开时&#xff0c;即using namespace std&#xff1b;例如放开标准c库…

5.2 基于深度学习和先验状态的实时指纹室内定位

文献来源 Nabati M, Ghorashi S A. A real-time fingerprint-based indoor positioning using deep learning and preceding states[J]. Expert Systems with Applications, 2023, 213: 118889.&#xff08;5.2_基于指纹的实时室内定位&#xff0c;使用深度学习和前一状态&…

让uniapp小程序支持多色图标icon:iconfont-tools-cli

前景&#xff1a; uniapp开发小程序项目时&#xff0c;对于iconfont多色图标无法直接支持&#xff1b;若将多色icon下载引入项目则必须关注包体&#xff0c;若将图标放在oss或者哪里管理&#xff0c;加载又是一个问题&#xff0c;因此大多采用iconfont-tools工具&#xff0c;但…

【php】php去除excel导入时的空格

背景 PHPExcel_1.8.0导入excel&#xff0c;遇到trim无法处理的空格。 解决方案 $excelVal preg_replace(“/(\s| | |\xc2\xa0)/”, ‘’, $excelVal); 完整代码 thinkphp5代码 function readExcel($file) {require_once EXTEND_PATH . PHPExcel_1.8.0/Classes/PHPExcel.p…

靶场实战(18):OSCP备考之VulnHub MY CMSMS

打靶思路 资产发现 主机发现服务发现漏洞发现&#xff08;获取权限&#xff09; 80端口/HTTP服务 组件漏洞URL漏洞3306端口/MySQL服务 组件漏洞口令漏洞80端口/HTTP服务 URL漏洞URL漏洞提升权限 www-data用户 sudosuidcron内核提权信息收集armour用户 sudo 1、资产发现 1.1…

考研C语言刷编程题篇之分支循环结构基础篇(一)

目录 第一题 第二题 方法一&#xff1a;要循环两次&#xff0c;一次求阶乘&#xff0c;一次求和。 注意&#xff1a;在求和时&#xff0c;如果不将sum每次求和的初始值置为1&#xff0c;那么求和就会重复。 方法二&#xff1a; 第三题 方法一&#xff1a;用数组遍历的思想…

【大数据处理技术实践】期末考查题目:集群搭建、合并文件与数据统计可视化

集群搭建、合并文件与数据统计可视化 实验目的任务一&#xff1a;任务二&#xff1a; 实验平台实验内容及步骤任务一&#xff1a;搭建具有3个DataNode节点的HDFS集群集群环境配置克隆的方式创建 Slave 节点修改主机名编辑 hosts 文件生成密钥免认证登录修改 hadoop 的配置文件编…

Java并发编程: 并发编程中的ExecutionException异常

一、什么是ExecutionException 在并发编程中在执行java.util.concurrent.Future实现类的get方法时&#xff0c;需要捕获java.util.concurrent.ExecutionException这个异常。Future.get()方法通常是要获取任务的执行结果&#xff0c;当执行任务的过程中抛出了异常&#xff0c;就…

ThinkPad T14/T15/P14s/P15s gen2电脑原厂Win10系统镜像 恢复笔记本出厂时预装自带OEM系统

lenovo联想原装出厂Windows10系统&#xff0c;适用型号&#xff1a; ThinkPad T14 Gen 2&#xff0c;ThinPad T15 Gen 2&#xff0c;ThinkPad P14s Gen 2&#xff0c;ThinkPad P15s Gen 2 &#xff08;20W1,20W5,20VY,20W7,20W0,20W4,20VX,20W6&#xff09; 链接&#xff1…

Redis在Windows10中安装和配置

1.首先去下载Redis 这里不给出下载地址&#xff0c;自己可以用去搜索一下地址 下载 下载完成后解压到D盘redis下&#xff0c;本人用的是3.2.100 D:\Redis\Redis-x64-3.2.100 2.解压完成后需要设置环境变量&#xff0c;这里新建一个系统环境变量中path 中添加一个文件所…

WCP知识分享平台的容器化部署

1. 什么是WCP? WCP是一个知识管理、分享平台,支持针对文档(包括pdf,word,excel等)进行实时解析、索引、查询。 通过WCP知识分享平台进行知识信息的收集、维护、分享。 通过知识创建、知识更新、知识检索、知识分享、知识评价、知识统计等功能进行知识生命周期管理。 wcp官…

第04章_IDEA的安装与使用(上)(认识,卸载与安装,JDK相关设置,详细设置,工程与模块管理,代码模板的使用)

文章目录 第04章_IDEA的安装与使用&#xff08;上&#xff09;本章专题与脉络1. 认识IntelliJ IDEA1.1 JetBrains 公司介绍1.2 IntelliJ IDEA 介绍1.3 IDEA的主要优势&#xff1a;(vs Eclipse)1.4 IDEA 的下载 2. 卸载与安装2.1 卸载过程2.2 安装前的准备2.3 安装过程2.4 注册2…

【小笔记】算法训练基础超参数调优思路

【学而不思则罔&#xff0c;思维不学则怠】 本文总结一下常见的一些算法训练超参数调优思路&#xff08;陆续总结更新&#xff09;&#xff0c;包括&#xff1a; batchsize学习率epochsdropout&#xff08;待添加&#xff09; Batch_size 2023.9.29 简单来说&#xff0c;较…