读书人

C++ premier - 错误、命名空间以及多重

发布时间: 2012-10-09 10:21:45 作者: rapoo

C++ premier -- 异常、命名空间以及多重继承

放了两个星期的暑假,回来久久不能进入状态。也或许是这一章对编程经验的要求远高于我目前的水平,很难静下心来看下去。总结也可能会因此做得很不到位。Anyway,有些东西就先记在这里吧。

第17章起就是高级主题了,主要涉及大型程序中使用的工具,包括异常的使用、命名空间以及多重继承。

?

1.异常
通过异常我们可以将问题的检测和问题的解决分享,这样程序的问题检测部分可以不必了解如何处理问题。


1.1抛出异常对象throw
在C++的异常处理中,需要由问题检测部分抛出一个对象给处理代码,通过这个对象的类型和内容,两个部分能够就出现了什么错误进行通信,这就是异常对象。异常以类似于将实参传给函数的方式抛出和捕获。
执行throw的时候,不会执行跟在throw后面的语句,而是将控制从throw转移到匹配的catch,该catch可以是同一函数中局部的catch,也可以在直接或间接调用发生异常的函数的另一个函数中。控制从一个地方传到另一个地方,这有两个重要含义:
(1)沿着调用链的函数提早退出。
(2)一般而言,在处理异常的时候,抛出异常的块中的局部存储不存在了。
异常对象由编译器管理,而且保证驻留在可以被激活的任意catch都可以访问的空间。
当抛出一个表达式的时候,被抛出对象的静态编译时类型将决定异常对象的类型。也就是说,假如抛出的对象是基类的解引用,而该指针实际指向继承类,该对象会被分割,成为事实上的基类对象。
抛出异常的时候,交暂停当前函数的执行,开始查找匹配的catch子句。首先检查throw本身是否在try块内部,如果是,检查与该try相关的catch子句,看是否其中之一与被抛出对象相匹配。如果找到匹配的catch,就处理异常;如果找不到,就退出当前函数(释放当前函数的内存并撤销局部对象),并且继续在调用函数中查找。如果对抛出异常的函数的请用是在try块中,则检查与该try相关的catch子句。如果找到匹配的catch,就处理异常;如果找不到匹配的catch,调用函数也退出,并且继续在调用这个函数的函数中查找,这个过程称之为“栈展开”。
如果一个块直接分配资源,而且在释放资源之前发生异常,在栈展开期间将不会释放该资源。

?

1.2捕获异常catch
catch子句中的异常说明符看起来像只包含一个形参的形参表,异常说明符是在其后跟一个可选形参名的类型名。类型必须是完全类型,不能向前声明。
在查找匹配的catch期间,找到的catch不必是与异常最匹配的那个catch,相反,将选中第一个可以处理该异常的catch。异常与catch说明符匹配的规则只允许以下转换:
(1)允许从非const到const的转换。也就是说,非const对象的throw可以与指定接受const引用的catch匹配。
(2)允许从派生类类型到基类类型的转换
(3)将数组转换为指向数组类型的指针,将函数转换为指向函数类型的适当指针。
通常,如果catch子句处理因继承而相关的类型的异常,它就应该将自己的形参定义为引用。(其规则与一般函数一样)
如果单个catch不能完全处理一个异常,在进行了一些校正行动之后,catch可能确定该异常必须由函数调用链中更上层的函数来处理,catch可以通过重新抛出将异常传递给调用链中更上层的函数。

?

1.3 函数测试块与构造函数
构造函数函数体内部的catch子句不能处理在处理构造函数初始化式时可能发生的异常。构造函数要处理来自构造函数初始化式的异常,唯一的方法是将构造函数编写为函数测试块。

struct Base1{Base1():ival(-11),dval(0), cval('b'){}void print( int )const;protected:int ival;double dval;char cval;private:int *id;};struct Base2{Base2(){}void print( double )const;protected:double fval;private:double dval;};struct Derived : public Base1{Derived():dval(0),sval("eva"){}void print( std::string ) const;protected:std::string sval;double dval;};struct MI: public Derived, public Base2{MI(){ival = new int(11);dvec.push_back(1);dvec.push_back(2);}void print( std::vector<double> );void bar();void foobar( double );protected:int *ival;std::vector<double> dvec;};void MI::bar(){int sval;//dval = 3.14;//此时编译出错,dval在Derived及Base2中均有定义//虽然dval在Base2中是私有成员,然而编译器先找到了这两个匹配的声明,//已经出现了歧义。cval = 'a';//id = 1;fval = 0;sval = *ival;}

虚继承是一种机制,类通过虚继承指出它希望共享其虚基类的状态。在虚继承下,对给定虚基类,无论该类在派生层次中作为虚基类出现多少次,只继承一个共享的基类子对象。
非虚继承的时候,我们通过向上逐步初始化各基类实现继承类的初始化,然而虚继承由于只继承一个共享的基类子对象,这使继承类的初始化变得复杂了起来:
在虚派生中,由最低层派生类的构造函数初始化虚基类。
无论虚基类出现在继承层次的任何地方,总是在构造非虚基类之前构造虚基类。

读书人网 >C++

热点推荐