1、try throw catch写法:
try {
if(条件为真)
throw ...;
... // 其余代码
}
catch (异常类型)
{
//处理代码
}
catch (...) // 捕获所有类型异常
{
...
}
int * point = new (std::nothrow) int;
// 禁止内存分配错误时输出异常,而是返回NULL指针。
在触发throw
时,发生栈解退:释放调用栈中内容直到遇到try块中的返回地址,将控制权交给块尾的异常处理程序catch
。而不继续执行throw后其余代码。
参考资料: C++ Primer Plus 15.3节
2、函数抛出异常(异常传递)与函数传参(参数传递)的区别:
1、throw抛出的永远是对象的拷贝(包括对象、引用、指针),无论该对象离开作用域时是否被析构(堆上变量、静态变量),因此和函数传参有区别,函数传引用参数时不需要拷贝。
2、catch()块接收异常类型不进行隐式类型转换(除基类派生类外),被调函数则可以进行(例:try抛出的int异常,不会被处理double异常的catch块捕获)。
3、异常捕获是按照catch块的顺序进行。
参考资料: More Effective C++ 条款12
3、通过引用捕获异常:
当写一个 catch 子句时,异常传递到 catch 子句中有三种方式:通过指针(by pointer),通过传值(by value)或通过引用(by reference),其中引用最好,下面详述。
a) 通过指针:
try{
...
static exception ex;
throw &ex; //方式一:抛出异常指针
throw new exception; //方式二:抛出异常指针
}
catch( exception *ex;){
...
}
通过指针的方式,对静态异常变量catch中不需要delete,但堆上异常变量需要delete,因此较复杂,同时不符合C++四个标准异常类型的语言规范。
b) 通过传值:
当异常通过传值时,需要进行拷贝两次(离开作用域一次,catch接收一次),而且它会产生 slicing problem,即派生类的异常对象被做为基类异常对象捕获时,那它的派生类行为就被切掉了(sliced off)。
c) 通过引用:
try{
...
exception ex;
throw ex; //方式一:抛出异常指针
}
catch( exception &ex;){
...
}
异常变量复制一次,避免了上述所有问题。
参考资料: More Effective C++ 条款13
4、审慎使用异常规格:
具体描述见链接,函数后加throw()
修饰称为异常规格,称对这个函数的异常安全性作出限制。
void fun(); //声明函数fun,表示fun可以抛出任何形式的异常。
void fun() throw(...); //表示fun可以抛出任何形式的异常。
void fun() throw(); //表示fun不允许抛出任何异常,即fun是异常安全的。
void fun() throw(exceptionType); //表示fun只能抛出exceptionType类型的异常。
这只是程序员的一种保证,实际也可能抛出异常。