1、异常(exception)的概念可以帮助我们将信息从检测到错误的地方传递到处理该错误的地方。如果函数无法处理某个问题,则抛出(throw)异常。并且寄希望于函数的调用者能直接或者间接地处理该问题。函数如果希望处理某个问题,可以捕获(catch)相应的异常。
2、一个结构完整的异常处理机制范例:
void taskmaster()
{
try{
auto result = do_task();
//使用result
}
catch(Some_error){
//执行do_task()时发生错误:处理该问题
}
}
int do_task()
{
//..
if(/* 能够执行该任务 */)
return result;
else
throw Some_error{};
}
3、强烈建议程序员使用自定义的专门用于表示错误的类型作为异常的对象。
4、传统的错误处理:
- 终止程序。例如:if(something_wrong) exit(1);
- 返回错误值。
- 返回合法值,而程序处于“错误”状态。
- 调用错误处理函数。
5、在异常处理的渐进决策是提供一种途径,如果一部分代码能检测到问题但是无法修复,那么它可以把该问题移交给系统中其他能够解决该问题的部分。
6、通过抛出异常终止某个操作后,程序仍然处于有效状态,称这个操作是异常安全的操作。有效状态是指构造函数已经完成且尚未执行析构函数的状态。
7、在具备构造函数和析构函数的属性后,可以使用局部对象管理资源的技术,称为“资源获取即初始化”(RAII),将这两个函数与错误处理机制有机地融合在一起。当涉及对象的指针而非局部对象时,我们使用unique_ptr和shared_ptr避免资源泄露。
8、有很多技术可以用于检查预置的条件和不变式。如果我们希望对检查的原因保持中立,则常使用断言(assertion,简写assert)。断言是一个逻辑表达式,我们假定断言的值为true。然而,断言不仅仅是一条注释,我们还需要注明一旦它的值为false时应该做什么。
9、C++标准提供了两种简单的机制:
- 在<cassert>中,标准库提供了assert(A)宏。当且仅当未定义宏NDEBUG(非调试)时,它在运行时检查断言A。一旦断言失败,编译器将输出一条错误信息并终止程序。
- C++语言使用static_assert(A, message)在编译时无条件检查断言A。一旦断言失败,编译器将输出message以及编译错误信息。
10、我们使用Assert::dynamic来与static_assert作为对比。Assert::dynamic的用法如下:
void f(int n)
//假定n应该在[1,max)之间
{
Assert::dynamic<Assert::level(2), Assert::Error>((n<=0||max<n),
Assert::compose(__FILE__, __LINE__, "range problem");
}
Assert::Error是默认的异常,我们可以无须显示地提及它。类似的,如果我们想使用默认的断言级别,也可以不需要显示地指出来:
void f(int n)
{
Assert::dynamic((n<=0||max<n), Assert::compose(__FILE__, __LINE__, "range problem");
}
11、我们可以throw任意类型的异常,前提是它能被赋值和移动。捕获的异常对象从本质上来说就是被抛出的对象的一份拷贝。
12、对于有些永远不会抛出异常,我们可以把它声明成noexcept的。
13、对于如下异常:
void f()
{
try{
throw E{}
}
catch(H){
//何时到达此处?
}
}
满足下列条件之一时,系统会调用异常处理程序:
- 如果H与E的类型相同;
- 如果H是E的无歧义的公有基类;
- 如果H和E都是指针类型,并且它们所指的类型满足以上2条;
- 如果H是引用类型,并且它所引用的类型满足1、2条。
14、重新抛出:捕获一个异常之后,异常处理程序发现它自己无法完整地处理该错误。此时,异常处理程序先完成在局部能完成的任务,然后再次抛出异常。我们用不带运算对象的throw表示重新抛出。注意:如果在没有异常的情况下强行重新抛出,则系统会调用std::terminate()。
15、捕获每个异常:在函数中省略号...表示“任意实参”,因此catch(...)的含义是“捕获任意异常”。例如:
void m()
{
try{
//...执行某些操作
}
catch(...){
//...清除...
throw;
}
}
16、一个try块可以对应多个catch从句(异常处理程序)。因为派生的异常能被多种异常类型的处理程序捕获,所以try语句中异常处理程序的书写顺序显得非常重要。程序将依次尝试处理每段代码。