1、
派生类对象与普通类对象的相同之处在于,可以直接访问该类的所有对象(包括this指针指向的对象和其他对象)的protected和private成员(包括其基类成员)。不同之处在于派生类对象只能访问其对应基类对象的protected成员(有隐式this指针传递),而不能访问其基类的其他对象的protect成员,而普通类对象则也可以直接访问该类所有对象的成员。
2、
在C++中,基类指针只能访问在该基类中被声明(或继承)的数据成员和成员函数(包括虚拟成员函数),而与它可能指向的实际对象无关,所以如果需要用基类指针来访问一个没有在该基类中声明但是又在其派生类中定义了的成员,则需要执行dynamic_cast来完成从基类指针到派生类指针的安全向下转换。把一个成员声明为虚拟的,只推延了“在程序执行期间根据指针指向的实际类类型,对于要调用实例的解析过程”
3、
关于基类,派生类的相关补充:
1、
派生表中指定的类必须先被定义好,方可被指定为基类。
2、
派生类的前向声明不能包括其派生表,而只需要类名即可。
3、
缺省的继承是private。
4、
继承而来的派生类的虚拟函数一般加上virtual较好,也可以省略。但基类中一定要声明为virtual。
5、
对于基类的静态成员,所有派生类对象都引用基类创建的这个相同,单一,共享的静态成员,而不是创建该派生类的另一个独立的静态成员。
6、
友员关系不会被继承,派生类没有成为“向它的基类授权友谊的类”的友员。
4、
继承机制下,派生类对象的构造函数(析构函数)调用顺序为:
1、
基类(子对象的)构造函数,若有多个基类,则以类派生表中出现的顺序为序。
2、
成员类对象的构造函数,若有多个成员类对象,则以它们在类定义中被声明的顺序为序。
3、派生类自己的构造函数。
4、派生类对象的析构函数的调用顺序与它的构造函数相反。继承机制下,析构函数的行为如下:派生类的析构函数先被调用,再静态调用基类的析构函数(从直接基类开始)。注意一般基类的析构函数不应该是protected,因为虚拟函数承接了“调用者所属类类型的访问级别”。作为一般规则,我们建议将类层次结构的根基类(声明了一个或多个虚拟函数)的析构函数声明为虚拟的。
5、
关于继承机制下基类构造函数(析构函数)相关的几点说明:
1、
作为一般规则,派生类构造函数应不能直接向一个基类的数据成员赋值,而是要把值传递给适当的基类构造函数来达到初始化赋值的目的。(一般是通过成员初始化表的方式)
2、
若基类不用于创建对象,则最好将其构造函数放在protect区,只允许其派生类对象调用;若基类只允许创建某一个特定的派生类类型的对象,则应该将基类的构造函数放在private区,并将此特定的派生类声明为该基类的友元来达到目的。
3、
派生类并不继承基类的构造函数,每个派生类都必须提供自己的构造函数集,派生类的构造函数只能合法的调用其直接基类的构造函数。(注意这里虚拟继承提供了一个特例:虚拟基类的初始化变成了最终派生类的责任)。
6、
关于虚拟函数的相关
1、
必须使用指针或者引用来支持虚拟函数机制(面向对象程序设计),基类对象由于其静态编译,故不会保留派生类的类型身份。
2、
第一次引入虚拟函数的基类时,必须在类体中将虚拟函数声明为virtual,但若在该基类外部定义该虚拟函数时不能指定virtual。该基类的派生类中该虚拟函数virtual可加可不加,但从多重继承考虑,最好加上。
3、
派生类改写的基类虚拟函数,其原型必须与基类虚拟函数完全匹配(包括const和返回值),但返回值有个特例:派生类实例的返回值可以是基类实例返回类型的公有派生类类型。
4、
纯虚拟函数(声明后紧跟=0,函数定义可写可不写)只是提供了一个可被其派生类改写的接口,其本身不能通过虚拟机制被调用,但可以静态调用(写了函数定义的虚基类的纯虚拟函数)。一般来说,虚拟函数的静态调用的目的是为了效率(避免动态绑定)。
5、
包含(或继承)了一个或多个纯虚拟函数的类被编译器识别为抽象基类,抽象基类不能用来创建独立的类对象,只能作为子对象出现在后续的派生类中。
6、通过基类指针来调用的虚拟函数的真正实例是在运行时刻确定的。但传给虚拟函数的缺省实参是在编译时刻根据被调用函数的对象的类型决定的(也即是若通过基类指针或引用调用派生类实例的虚拟函数,则传递给它的缺省实参是由基类指定的)。
7、
虚拟继承和多继承相关:
1、
虚拟继承主要实为了解决继承了多个基类实例,但是只需要一份单独的共享实例的情况。
2、
非虚拟派生中,派生类只能显式的初始化其直接基类(即派生类只能调用其直接基类的构造函数),而在虚拟派生中,虚拟基类的初始化变成了最终派生类的责任,这个最终派生类是由每个特定的类对象声明来决定的,其非虚拟基类的初始化同非虚拟派生一样,只能由其直接派生类完成。(即中间派生类的对于虚拟基类构造函数的调用被抑制)。
3、
虚拟继承下构造函数的调用顺序按直接基类的声明顺序,对每个继承子树作深度优先遍历。第一步按此顺序调用所有虚拟基类的构造函数;第二步按此顺序调用非虚拟基类的构造函数。析构函数的调用顺序与构造函数相反。
4、
虚拟基类成员的可视性,对于虚拟基类成员的继承比该成员后来重新定义的实例权值(优先级)小,故特化的派生类实例名覆盖了共享的虚拟基类的实例名。而在非虚拟派生下的解析引用过程,每个继承得到的实例都有相同的权值(优先级)。
5、
继承下派生类的类域被嵌套在基类类域中,若一个名字在派生类域中没有被解析出来,则编译器在外围基类域中查找该名字定义。在多继承下,名字解析查找过程为先是在本类类域中查找,再对继承子树中的所有基类同时查找,每个继承得到的实例都有相同的权值(优先级)。若在两个或多个基类子树中都找到了该名字,则对其的使用是二义的。