c++ 中 try catch throw异常

C++中处理异常的过程是这样的:在执行程序发生异常,可以不在本函数中处理,而是抛出一个错误信息,把它传递给上一级的函数来解决,上一级解决不了,再传给其上一级,由其上一级处理。如此逐级上传,直到最高一级还无法处理的话,运行系统会自动调用系统函数terminate.

格式:

 try

{

}

catch(...)  //表示任何异常

{

}

如果在catch 中没有调用throw 再次抛出异常,说明该异常在catch中被吞掉,不会再传出调用该函数之外。

throw:

1.throw抛出的永远是对象的拷贝(包括普局部变量、引用,静态变量)。不管该对象离开作用域时是否被析构(堆上变量、静态变量);或者被抛出的对象不会被释放,也会进行拷贝操作,如抛出的对象是静态变量,或者是引用)。

因此和函数传参有区别,函数传引用参数时不需要拷贝。

2.当异常对象被拷贝时,拷贝操作是由对象的拷贝构造函数完成的。该拷贝构造函数是对象的静态类型(static type)所对应类的拷贝构造函数,而不是对象的动态类型(dynamic type)对应类的拷贝构造函数。

class Widget { ... };
class SpecialWidget: public Widget { ... };
void passAndThrowWidget()
{
SpecialWidget localSpecialWidget;
...
Widget& rw = localSpecialWidget; // rw 引用SpecialWidget
throw rw; //它抛出一个类型为Widget
// 的异常
}
这里抛出的异常对象是Widget,即使rw引用的是一个SpecialWidget。因为rw的静态类型(static
type)是Widget,而不是SpecialWidget。

3. 

catch (Widget& w) // 捕获Widget异常
{
... // 处理异常
throw; // 重新抛出异常,让它
} // 继续传递
catch (Widget& w) // 捕获Widget异常
{
... // 处理异常
throw w; // 传递被捕获异常的
} // 拷贝
第一个块中重新抛出的是当前异常(current exception),无论它是什么类型。特别是如果这个异常开始就是做为SpecialWidget类型抛出的,那么第一个块中传递出去的还是SpecialWidget异常,即使w的静态类型(static	type)是Widget。这是因为重新抛出异常时没有进行拷贝操作。第二个catch块重新抛出的是新异常,类型总是Widget,因为w的静态类型(static type)是Widget。一般来说,你应该用throw来重新抛出当前的异常,因为这样不会改变被传递出去的异常类型,而且更有效率,因为不用生成一个新拷贝。

个人觉得:throw最好抛出的是对象。 

执行throw后,如果在当前函数没有执行catch进行捕捉的话,throw后面的代码就不会被执行,所以在要注意内存泄漏的问题。

就算上一次函数执行了catch捕捉异常,原来的throw之后的代码也不会执行。

所以

1.在构造函数中抛出了异常,该构造函数就不能完整执行完,对象构造不成功。

2.在析构函数中抛出异常,存在内存泄漏。

延伸:函数声明后面加throw()的作用

void fun() throw();      //表示fun函数不允许抛出任何异常,即fun函数是异常安全的。

void fun() throw(...);    //表示fun函数可以抛出任何形式的异常。

void fun() throw(exceptionType);    // 表示fun函数只能抛出exceptionType类型的异常。

catch

1.catch()块接收异常类型不进行隐式类型转换(除了以下2种情况),被调函数则可以进行(例:try抛出的int异常,不会被处理double异常的catch块捕获)。

a.第一种是继承类与基类间的转换

b.一个类型化指针(typed pointer)转变成无类型指针(untyped pointer),所以带有const void* 指针的catch子句能捕获任何类型的指针类型异常。

2.异常传递到 catch 子句中有三种方式:通过指针(by pointer),通过传值(by value)或通过引用(by reference),其中引用最好.

a.通过指针的方式,对静态异常变量catch中不需要delete,但堆上异常变量需要delete,因此较复杂。而且通过指针捕获异常也不符合C++语言本身的规范。

b.通过传值时,需要进行拷贝两次(离开作用域一次,catch接收一次),而且它会产生 slicing problem(切割问题),即派生类的异常对象被做为基类异常对象捕获时,那它的派生类行为就被切掉了(sliced off)。这样的sliced对象实际上是一个基类对象:它们没有派生类的数据成员,而且当本准备调用它们的虚拟函数时,系统解析后调用的却是基类对象的函数。

c.异常变量复制一次,避免了上述所有问题。

延伸:

四个标准的异常

bad_alloc(当operator new(参见条款M8)不能分配足够的内存时,被抛出),

bad_cast(当dynamic_cast针对一个引用(reference)操作失败时,被抛出),

bad_typeid(当dynamic_cast对空指针进行操作时,被抛出)

bad_exception(用于unexpected异常;参见条款M14)――都不是指向对象的指针。

CException是Microsoft基本类库中处理各种异常的基础库。

CException是一个抽象基类,不可构造一个CException对象.

其派生库及描述如下:

CMemoryException

内存不够
CNotSupportedException请求不支持的操作
CArchiveException文档指定异常
CFileException文件指定异常
CResourceExceptionWindows资源未找到或不可创建
COleExceptionOLE异常
CDBException数据库异常(即基于开放数据库连接的MFC数据库类出现异常)
COleDispatchExceptionOLE发送(自动)异常
CUserException资源无法找到
CDaoException数据访问对象异常(即DAO类出现异常)
CInternetExceptionInternet异常(即Internet类出现异常)

1.可以在try{}中手动抛出异常throw/

2.可以使用多个catch来捕捉异常。

3.可以调用GetErrorMessage或ReportError来向用户报告异常的详细情况。

4.如果Catch关键字得到异常,则不会自动删除。需调用delete函数。

CException

构造一个CException对象
Delete删除一个CException对象
GetErrorMessage获取异常描述信息
ReportError在消息框中向用户报告一个错误信息

 try

{

}

catch(CException *e)  //

{

   e->ReportError();

   e->delete();

}

使用的场景有:原则是容易抛出异常地方,如

1.内存操作的,分配、释放、移动

2.序列化读写文件CArchive

3.使用第三方接口读写excel文件。

bad_alloc 是operator new不能满足内存分配请求时抛出的异常类型。

 try

{

}

catch(bad_alloc  &e)  //

{

}

 try

{

}

catch(std::bad_alloc  &)  //

{

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值