1 类的继承方式有public(共有继承)、protected(保护继承)、private(私有继承)三种。不同的继承方式,导致原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同。这里说的访问来自两个方面:一是派生类中的新增成员访问从基类继承的成员。二是在派生类外部(非类族内的成员),通过派生类的对象访问从基类继承的成员。
2 类型兼容规则是指在需要基类对象的任何地方,都可以使用共有派生类的对象来代替。派生类的对象、引用、指针可以初始化或隐含转换为基类的指针、引用、对象。在替代之后,派生类对象就可以作为积累的对象使用,但只能使用从基类继承的成员。多态的设计方法可以保证在类型兼容的前提下,基类、派生类分别以不同的方式来响应相同的消息。
3构造派生类的对象时,就要对基类的成员对象和新增成员对象进行初始化。
#include<iostream> using namespace std; class Base{ public: Base1(int i){cout<<"Constrcting Base1"<<i<<endl;} }; class derived:public Base{ public: Derived(int a,int b):Base(a),member(b){} private: Base member; };
如果要为派生类编写复制构造函数,一般需要为相应的基类传递参数。
派生类析构函数的声明方法与没有继承关系的类中析构函数的声明方法完全相同,只要把函数体中负责把派生类新增的非对象成员的清理工作做好就够了,系统会自己调用基类及对象成员的析构函数来对基类及对象成员进行清理。
3 派生类成员的标识与访问
在对派生类的访问中,实际上有两个问题需要解决:第一是唯一标识问题,第二是成员本身的属性问题,严格来说应该是可见性问题。我们只能访问一个能够唯一标识的可见成员。如果通过某一个表达式能引用的成员不只一个,称为二义性。
作用域分辨符::。如果某派生类的多个基类拥有同名的成员,同时,派生类有新增这样的同名成员,在这种情况下,派生类成员将隐藏所有基类的同名成员。对基类同名成员的访问,只能通过基类名和作用域分辨符来实现,也就是说,必须明确告诉系统要使用哪个基类的成员。
如果某个派生类的部分或全部直接基类是从另一个共同的基类派生而来,在这些直接基类中,从上一级基类继承来的成员就拥有相同的名称,因此派生类中也就会产生同名现象,对这种类型的同名成员也要使用作用域分辨符来唯一标识,而且必须用直接基类来进行限定。也可以将共同基类设置为虚基类,这是从不同的路径继承过来的同名数据成员在内存中就只有一个副本,同一个函数名也只有一个映射。虚基类声明形式为:
class 派生类名:virtual 继承方式 基类名
在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。声明了虚基类之后,虚基类的成员在进一步派生过程中和派生类一起维护同一个内存数据副本。
#include<iostream> using namespace std; class Base0{ public: int var0; void fun0(){cout<<"member of Bade0"<<endl;} }; class Base1:virtual public Base0{ public: int var1; }; class Base2:virtual public Base0{ public: int var2; }; class Derived:public Base1,public Base2{ public: int var; void fun(){cout<<"Member of Derived"<<endl;} }; int main(){ Derived d; d.var0=2; //直接访问虚基类的数据成员 d.fun0(); //直接访问虚基类的函数成员 return 0; }
如果虚基类声明有非默认形式的(即带形参的)构造函数,并且没有声明默认形式的构造函数。这时,在整个继承关系中,直接或间接继承虚基类的所有派生类中,都必须在构造函数的成员初始化表中列出对虚基类的初始化。建立一个对象时,如果这个对象中含有从虚基类继承来的成员,则虚基类的成员是有最远派生类的构造函数通过调用虚基类的构造函数进行初始化的。而且,只有最远派生类的构造函数会调用虚基类的构造函数,该派生类的其他基类对虚基类构造函数的调用将自动被忽略。