C++异常处理
admin
2024-04-08 02:04:10
0

1.传统的C语言错误处理方法



    (1)在函数中返回一个错误信息。比如Linux经常返回-1代表错误。
    也可以设定一个全局的变量,比如errno
    (2)用信号函数signal和raise捕捉信号
    (3)用setjmp和longjmp两个非局部跳转函数,这种方法很困难,耦合度也很高,因为它和goto不一样,他会跳转到其他地方,而不是局部地点。
    以上三种方法都有自己的问题。第一个显得代码过于冗长,第二个信号处理标准不一致就会出很大问题,第三个问题更大,因为它会跳到栈区的其他地方,不会自动调用析构函数,行为危险,结果未知。


2.C++的异常处理方式


2.1 try-throw-catch结构


   try代码块负责运行正常代码,在代码中的错误会通过throw抛出,catch负责接受抛出的错误,做出相应的处理。

(1)throw:抛出异常

#include
#include
//抛出一个异常
//设置一个错误类
class MyError{
private:const char* const data;
public:MyError(const char* const msg = 0):data(msg){}//构造函数};void f()
{throw MyError("something bad happened");
}int main()
{f();
}
//f()函数抛出一个错误,生成了MyError对象,这是程序创建的一个对象的拷贝,f()函数返>回了这个对象。

2.2.捕获异常 catch


catch(type value)

{

        some code       

}


2.3.try块


在try块中编写的代码,所产生的错误都会被根据程序员所设计的throw所抛出,程序会返回到错误开始的地方,如果不对错误做出处理,程序到此就结束了。
    try的使用方法为
    try{
        代码
    }


2.4异常处理器


    处理抛出的异常的地方就叫异常处理器,他会接住throw抛出的异常,异常处理对继承和多态同样有效

使用方法:
    try{
        代码
    }
    catch(类型1 id1)
    {
        处理方法1
    }
    catch(类型2 id2)
    {
        处理方法2
    }
    ……
    异常抛出后,会一一匹配catch,如果类型符合,就能够执行catch块的语句

    异常被抛出以后,会按照顺序匹配异常处理器,一旦找到能够匹配的,就会开始处理,不会继续匹配下一个,即使下面的更加合适。
    匹配一个异常,并不要求异常和处理器之间完全相关,一个对象或者是指向派生类对象的引用都会与其基类处理器匹配。如果不是引用或者指针,而是派生类对象和基类相匹配,
    则会发生截断,派生类对象会被截断,与基类相匹配。

#includeclass X{
public:class Trouble{};class Small:public Trouble{};class Big:public Trouble{};void fun(){throw Big();}};int main(){X x;try{x.fun();}catch(X::Trouble&){std::cout << "捕获到Trouble"<

输出:捕获到Trouble

Small,Big都是Trouble的派生类,抛出Big异常处理的时候,却被第一个异常处理器捕获,不会被第三个捕获。

所以我们在处理异常的时候,可以把基类处理器放在最后面捕获一些未定位的错误。
    注意:异常匹配过程不会发生类型转化,以下面的程序为例子

定义了两个类,Except1和Except2,其中Except2定义了一个自动转换类型的函数。

在抛出异常后,异常处理器接受到抛出的错误类型,会匹配到Except1而不是Except2,它不会自动把Except1转化为Except2。

#include
using std::cout;class Except1{};
class Except2{
public:Except2(const Except1&){}//类型转换,将Except1转换为其他类型
};
void fun()
{throw Except1();//抛出一个异常,类型是Except1
}
int main()
{try{fun();}catch(Except2&){cout << "inside catch(except2)\n";}catch(Except1&){cout << "inside catch(except1)\n";}return 0;
}

运行结过将会是:inside catch(except1)


2.4.1捕获所有的异常


    catch(…)
    {

        some code
    }

    这个表示捕获所有的异常,但是不会接受任何参数,可以把它放在异常处理器的最后面。


2.4.2 不捕获异常


    抛出的异常不能被忽略,需要被捕获,否者就会出错。
    如果没有异常处理器可以捕获抛出的异常,则会进行下面的处理
    (1)terminate()函数
    如果没有可以匹配异常的异常处理器,则会调用这个函数,这个函数调用了C语言标准库中的abort()函数,程序不会被正常终止,不会调用析构函数
    除了异常不会被捕获会调用terminate()函数,还有另外两种情况会调用这个函数
    第一种情况:局部对象的析构函数抛出异常
    第二种情况:静态对象的构造函数或者析构函数抛出异常
    (2)set_terminate()函数
    程序员可以使用set_terminate定义自己的terminate()函数,但是在调用结束后,仍然会调用默认的terminate()函数。
    使用方法如下。

#include
#include
using namespace std;
void terminator()
{cout << "terminator 回来了\n";
}
//设置一个函数指针,指向set_terminator
void (*Myterminator)(void)  = set_terminate(terminator);
class Botch
{
public:class Fruit{};void fun(){cout << "Botch::f()"<

set_terminate函数是一个这样的函数 void set_terminate(void(*func)(void));
其中func是自己定义的
析构函数抛出异常,catch获取了异常,先调用了自己定义的set_terminate()。但是运行结果和我们预料的不一致。

输出:
Botch::f()
terminator 回来了
Aborted (core dumped
)

第一次调用fun函数的时候,确实调用了自定义的terminate函数,但是析构函数所产生的异常却仍然调用了默认的terminate函数,终止了程序。

说明,析构函数抛出的异常,只会调用默认的terminate函数。我们在编写代码的时候,应该避免让构造函数抛出异常。


二.清理


2.1 构造函数抛出异常


有时候异常发生了,我们需要对资源进行清理,比如在构造函数中抛出异常,由于构造函数并没有完成,异常发生后,它不会自动调用析构函数清理资源

#include
using namespace std;class Cat{
public:Cat(){ cout << "cat()" <

 运行结果:
UseResource()
cat()
cat()
cat()
Dog模拟内存分配
n = 44
inside hander

cat的构造函数被调用,Dog却在分配内存的时候抛出一个异常,导致UseResource的构造函数没有结束,所以最后的析构函数没有调用,cat分配的资源仍然没有被释放


2.2管理资源的方法



    为了防止资源泄露,可以使用两种办法来来分配资源
    (1)在构造函数里捕获异常,用于释放资源
    (2)在对象的构造函数分配资源,并且在对象的析构函数中释放资源
  

#include
#include
using namespace std;template
class PWrap
{
private:T* ptr;
public:class RangeError{};//异常类PWrap(){ptr = new T[sz];cout << "PWrap constructor"<= 0 && i < sz)return ptr[i];throw RangeError();}
};class Cat{
public:Cat(){cout << "Cat()" << endl;}~Cat(){cout << "~Cat()"< cats;PWrap dog;};int main()
{try{UseResource ur;}catch(int){cout << "inside handler" << endl;}catch(...){cout << "inside catch(...)" << endl;}return 0;
}


这种方法的好处在于在创建UseResource对象之前,指针类就已经被创建了,所需要创建的对象嵌入到了指针对象中。当Dog出现异常时,cat的析构函数被调用了,就不会有
内存泄露。
输出:
Cat()
Cat()
Cat()
PWrap constructor
动态创建Dog对象
~Cat()
~Cat()
~Cat()
PWrap destructor
inside handler

这样的方法实际上也用于智能指针。

2.3 函数级的try块


#include
using namespace std;
int main()try
{throw "主函数抛出异常";}
catch(const char* msg){cout << msg << endl;}

在函数后面使用try,然后紧跟着函数使用catch接受抛出的异常,这种方法也可以用在类的内部,比如用在构造函数里面。

相关内容

热门资讯

祥龙订购APP(四川禄宏商品)...   以“祥龙订购”APP为例,其打着“贵金属商品订购”的幌子,实则从事非法期货交易。该平台通过网络大...
重庆银行上半年净利预增超10%... 7月20日晚间,渝农商行(601077.SH)、重庆银行(601963.SH)及浦发银行(60000...
原创 我... 港口上集装箱堆得像小山,工厂里流水线一刻不停,可走进商场,柜台前的人却没那么多。这种反差,几乎每个中...
国货香氛想用线香赶超国际品牌 界面新闻记者 | 周芳颖 界面新闻编辑 | 楼婍沁 消费者梁琦告诉界面时尚,每天上班前,她都会在...
韩股去杠杆已近尾声?融资余额降... 财联社7月21日讯(编辑 史正丞)最新数据显示,韩国股市近一个月激烈的“去杠杆”进程,已经将韩国股民...
外贸找货别只会上1688,这几... 刚做外贸那会儿,我也以为找个工厂就是打开1688搜一下的事。结果要么找到的是贸易商,转手加价30%;...
京东“业采融合”模式入选埃森哲... 近日,2026 世界人工智能大会(WAIC)在上海举办,京东以“AI 进入物理世界”为核心主题,集中...
长鑫科技最新公告 7月21日晚间,长鑫科技集团股份有限公司(简称“长鑫科技”)发布首次公开发行股票并在科创板上市的公告...
张柏芝被曝得知谢贤病重后 提前... (来源:梨视频) 据港媒报道,得知谢贤病重之时,张柏芝已火速安排远在澳洲上学的大儿子谢振轩(Luca...
最新中国500强出炉!15家豫... 据河南发布,今年500家上榜的中国公司在2025年的总营业收入达到14.26万亿美元,接近中国当年G...
哈尔滨ICL晶体植入:就诊机构... 随着屈光矫正技术持续迭代升级,近视手术凭借微创、恢复快的特点,成为学生、上班族、体检人群主流的摘镜方...
主力资金丨主力大手笔加仓名单出... 14个行业获主力资金净流入。 据证券时报·数据宝统计,今日(7月21日)沪深两市主力资金净流入170...
美股存储芯片大涨:闪迪涨超10... 记者|杜宇 编辑|何小桃 杜恒峰 校对|金冥羽 北京时间7月21日晚,美股三大指数高开后震荡。 截至...
医院送药机器人是什么?三甲医院... 随着国内智慧医院数字化改造持续推进,药房作为全院药品流转核心枢纽,人工配送模式的短板逐步凸显。高峰时...
又一百亿估值AI独角兽诞生!被... @智东西 作者 | 茄子 编辑 | 程茜 智东西7月21日消息,昨日,英国AI材料发现初创公司Cus...
*ST亿晶预重整的算盘与隐患:... 有着“A股光伏组件第一股”之称*ST亿晶(600537.SH),日前披露了上交所对其签署重整投资协议...
侨银股份、润建股份在广州成立合... 每经AI快讯,天眼查App显示,近日,拓肯(广东)技术服务有限公司成立,法定代表人为韩霜铭,注册资本...
袁记云饺港股上市获备案:年入2... 《星岛》记者 陈奇杰 深圳报道 7月17日,证监会网站显示,袁记食品集团股份有限公司(下称“袁记云饺...
小哥引擎完成A轮融资 2026年7月20日,上海小哥引擎网络技术有限公司(简称“小哥引擎”)宣布完成A轮融资。此次融资将主...