1、类成员的指针不同于指向普通数据或函数的指针,普通指针只根据对象或函数的类型而变化,而成员的指针必须反映成员所属的类。
2、异常是通过抛出对象而引发的。该对象的类型决定应该激活哪个处理代码。被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那个。异常以类似于将实参传递给函数的方式抛出和捕获。异常可以是可传给非引用形参的任意类型的对象,这意味着必须能够复制该类型的对象。
传递数组或函数类型实参的时候,该实参自动转换为一个指针。被抛出的对象将发生同样的自动转换,因此,不存在数组或函数类型的异常。相反,如果抛出一个数组,被抛出的对象转换为指向数组首元素的指针,类似地,如果抛出一个函数,函数被转换为指向该函数的指针。
3、一般而言,在处理异常的时候,抛出异常的块中的局部存储不存在了。当抛出一个异常的时候,被抛出对象的静态编译时类型将决定异常对象的类型。无论指针所指的对象的实际类型是什么,异常对象的类型都与指针的静态类型相匹配。
4、抛出指针通常是个坏主意。
5、寻找匹配catch的过程中,如果找不到,则退出当前函数(释放当前函数的内存并撤销局部对象),并且继续在调用函数中查找。
6、栈展开(stack unwinding):沿嵌套函数调用链继续向上,直至为异常找到一个catch子句。
7、析构函数应该从不抛出异常。在为某个异常进行栈展开的时候,析构函数如果又抛出自己的未处理的另一个异常,将会导致调用标准库terminate函数。一般而言,terminate函数将调用abort函数,强制从整个程序非正常退出。
8、如果找不到匹配的catch,程序就调用库函数terminate。
9、在查找匹配的 catch 期间,找到的 catch 不必是与异常最匹配的那个catch,相反,将选中第一个找到的可以处理该异常的 catch。因此,在 catch 子
句列表中,最特殊的 catch 必须最先出现。
10、除下面几种可能的区别之外,异常的类型与catch说明符的类型必须完全匹配:
• 允许从非 const 到 const 的转换。也就是说,非 const 对象的 throw可以与指定接受 const 引用的 catch 匹配。
• 允许从派生类型型到基类类型的转换。
• 将数组转换为指向数组类型的指针,将函数转换为指向函数类型的适当指
针。
在查找匹配 catch 的时候,不允许其他转换。具体而言,既不允许标准算术
转换,也不允许为类类型定义的转换。
11、如果catch子句处理因继承而相关的类型的异常,它就应该将自己的形参定义为引用。
12、空throw语句将重新抛出异常对象,它只能出现在catch或从catch调用的函数中。如果在处理代码不活动时碰到空 throw,就调用 terminate 函数。
被抛出的异常是原来的异常对象,而不是catch形参。当 catch 形参是基类类型的时候,我们不知道由重新抛出表达式抛出的实际类型,该类型取决于异常对象的动态类型,而不是 catch 形参的静态类型。例如,来自带基类类型形参 catch的重新抛出,可能实际抛出一个派生类型的对象。
一般而言,catch 可以改变它的形参。在改变它的形参之后,如果 catch 重 新抛出异常,那么,只有当异常说明符是引用的时候,才会传播那些改变。
示例
catch (my_error &eObj) { // specifier is a reference type eObj.status = severeErr; // modifies the exception object throw; // the status member of the exception object is severeErr } catch (other_error eObj) { // specifier is a nonreference type eObj.status = badErr; // modifies local copy only throw; // the status member of the exception rethrown is unchanged }
13、捕获所有异常
catch(...)
14、在进入构造函数函数体之前处理构造函数初始化式,构造函数函数体内部的catch 子句不能处理在处理构造函数初始化时可能发生的异常。为了处理来自构造函数初始化式的异常,必须将构造函数编写为函数try块。可以使用函数测试块将一组 catch 子句与函数联成一个整体。
示例
template <class T> Handle<T>::Handle(T *p) try : ptr(p), use(new size_t(1)) { // empty function body } catch(const std::bad_alloc &e) { handle_out_of_memory(e); }
15、exception类型所定义的唯一操作是一个名为what的虚成员,该函数返回const char*对象,它一般返回用来在抛出位置构造异常对象的信息。
16、异常安全(exception safe)
如果在类的成员函数/或普通函数中申请了内存,而在执行函数的中途出现异常,则不会执行程序末尾的释放内存的语句,这会出问题,称为异常不安全。
所谓异常安全是指,即使发生异常,程序也能正确操作,被分配的任何资源都适当地释放。
通过定义一个类来封装资源的分配和释放,可以保证正确释放资源,这一技术称为“资源分配即初始化”,称为RAII。
示例
class Resource { public: Resource(parms p): r(allocate(p)) { } ~Resource() { release(r); } // also need to define copy and assignment private: resource_type *r; // resource managed by this type resource_type *allocate(parms p); // allocate this resource void release(resource_type*); // free this resource };
可能存在异常的程序以及分配资源的程序应该使用类来管理那些资源。