异常处理是一种允许两个独立开发的程序组件在程序执行期间遇到程序不正常的情况(异常exception)时相互通信的机制。本文总结了19个C++(www.cppentry.com)异常处理中的常见问题,基本涵盖了一般C++(www.cppentry.com)程序开发所需的关于异常处理部分的细节。
1. throw可以抛出哪些种类的异常对象?如何捕获?
1)异常对象通常是一个class对象, 通常用以下代码抛出:
// 调用的类的构造函数
throw popOnEmpty();
但是throw 表达式也可以抛出任何类型的对象, 例如(虽然很不常见)在下面的代码例子中,函数mathFunc()抛出一个枚举类型的异常对象
enum EHstate { noErr, zeroOp, negativeOp, severeError };
int mathFunc( int i )
{
if ( i == 0 )
throw zeroOp; // 枚举类型的异常
}
2)抛出异常的语句或其调用函数要在try块中才能被捕获。
2. catch子句的语法
一个catch 子句由三部分构成:
1)关键字catch
2)异常声明,在括号中的单个类型或单个对象声明被(称作异常声明,exception declaration)
3)复合语句中的一组语句。
// stackExcp.h
class popOnEmpty { };
class popOnFull { };
catch ( pushOnFull )
{
cerr 《 "trying to push a value on a full stack\n";
return errorCode88;
}
3. 异常声明可以只是一个类型声明而不是对象声明吗?
catch 子句的异常声明可以是一个类型声明或一个对象声明。当我们要获得throw 表达式的值或者要操纵throw 表达式所创建的异常对象时,我们应该声明一个对象。
catch ( pushOnFull eObj )
{
cerr 《 "trying to push the value " 《 eObj.value() 《 " on a full stack\n";
}
4. 异常声明中异常对象的拷贝过程?
catch 子句异常声明的行为特别像参数声明。同理,也可以分出按值传递和引用传递(指针)。通常采用的是引用传递。
例1:按值传递。当进入catch 子句时,如果异常声明声明了一个对象,则用该异常对象的拷贝初始化这个对象。例中对象eObj 是用异常对象的值来初始化的,会调用拷贝构造函数。
void calculate( int op ) {
try {
mathFunc( op );
}
catch (pushOnFull eObj ) {
// eObj 是被抛出的异常对象的拷贝
}
}
例2:引用传递。catch子句可以直接引用由throw 表达式创建的异常对象,而不是创建一个局部拷贝。可以防止不必要地拷贝大型类对象。
void calculate( int op ) {
try {
mathFunc( op );
}
catch (pushOnFull &eObj ) {
// eObj 引用了被抛出的异常对象
}
}
5. 异常处理的栈展开过程是什么?
在查找用来处理被抛出异常的catch 子句时,因为异常而退出复合语句和函数定义,这个过程被称作栈展开(stack unwinding)。随着栈的展开,在退出的复合语句和函数定义中声明的局部变量的生命期也结束了。C++(www.cppentry.com)保证,随着栈的展开,尽管局部类对象的生命期是因为抛出异常而被结束,但是这些局部类对象的析构函数也会被调用。
6. 异常抛出没有在try块中或抛出的异常没有对应的catch语句来捕捉,结果如何?
异常不能够保持在未被处理的状态,异常对于一个程序非常重要,它表示程序不能够继续正常执行。如果没有找到处理代码,程序就调用C++(www.cppentry.com)标准库中定义的函数terminate()。terminate()的缺省行为是调用abort() ,指示从程序非正常退出。
7.为什么要重新抛出异常?怎么写?
在异常处理过程中也可能存在"单个catch 子句不能完全处理异常"的情况。在对异常对象进行修改或增加某些信息之后,catch 子句可能决定该异常必须由函数调用链中更上级的函数来处理。表达式的形式为:throw;
例子如下:
try
{
entryDescr->checkMandatoryData(beModel_);
}
catch (CatchableOAMexception & error) // 只能用引用声明
{
vector<string> paramList;
paramList.push_back(currentDn);
error.addFrameToEnd(6,paramList); // 修改异常对象
throw; //重新抛出异常, 并由另一个catch 子句来处理
}
注意1:被重新抛出的异常就是原来的异常对象,所以异常声明一定要用引用。
注意2:在catch 语句里也可以抛出其它