Error(const Error &error) = default; // 使用合成的拷贝构造函数
private:
string _error_type;
}
// 主函数
void run()
{
// ...
if(book_a.GetISBN() == book_b.GetISBN()) // book_a book_b 假设已经定义
{
// 继续后面的处理
}
else
{
throw Error("错误!两本书的ISBN不一样,不能进行比较!"); // 抛出异常
}
}
// 主程序
int main()
{
Init(); // 初始化系统
while(1)
{
/************************************************************/
* 将可能抛出异常的代码用 try 块包含 *
* 一旦 try 中的代码抛出异常 跟随 try 后面的 catch 便会捕捉到抛出 *
* 的异常。(如果没有匹配的catch块就继续往上一层搜索,如果最后没有任 *
* 何相匹配的catch,则程序直接终止。 *
*************************************************************/
try
{
run(); // 运行系统
}
catch(Error e)
{
// 处理异常
}
}
return 0;
}
另外需要注意的三点说明:
注意catch
块的接收顺序,尤其是异常类型是具有继承关系的类型。
class Base;
class Derived:public Base;
void f()
{
try
{
throw Derived(); // 抛出 Derived 类型的异常
}
catch(Base b)
{
// 异常会被 Base 接收。(进行了隐式类型转换。这是和赋值操作一样的嘛,很好理解。)
}
catch(Derived d)
{
// 异常不会进入这里
}
}
catch会进行的隐式类型转换只有三种:常量->非常量、子类->父类、数组->指向数组首元素的指针(函数类似)。其它的任何类型都不会隐式转换,比如catch(int) 不能就收double类型的异常对象。
重抛出
如果直接throw;
不跟异常对象,那么就会抛出当前作用域内的异常对象,如果当前作用域没有则抛出上层作用域内的对象。
try
{
// ...
}
catch(Error e)
{
// ...
throw ; // 相当于 throw e;
}
捕获所有的异常
如果catch
的参数是...
即catch(...)
则这个catch块将接收当前作用域内(包括嵌套的内存作用域)之前所有没被接收的异常对象。
异常处理的其它相关细节
构造函数初始化列表抛出异常
class Test
{
public:
Test(Object ob)
:_ob(bo) // 如果此时抛出异常,那么构造函数内的异常处理并不能接收到
{ // 这个异常将会跑到构造Test类的作用域中,而且未构造完的Test对象并不会析构
// ...
try
{
}
catch
{
}
}
private:
Object _ob;
}
替换的方法如下:
class Test
{
public:
Test(Object ob) try:_ob(bo)
{
// ...
}
catch(...) // catch既能接收初始化列表抛出的异常
{ // 也能够接收构造函数块里面的异常
}
private:
Object _ob;
}
noexcept说明符
noexcept
的意思是不抛出异常
void f()noexcept
{
}
如果声明了 noexcept
的函数抛出了异常,则程序直接终止。局部对象是否被释放是未定义的。