《继承》
一丶定义:
在C++语言中,一个派生类可以从一个基类派生,也可以从多个基类派生。从一个基类派生的继承称为单继承;从多个基类进程派生的称为多继承。
类的继承,就是新的类从已有类那里得到已有的属性,从已有类产生新类的过程就是类的派生。
原有的类称为基类或者父类,产生的类称为派生类或者子类。
二丶继承的方式:
1.公有继承(public)
当类的继承方式为公有继承时,基类的公有成员和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问。
2.保护继承(protect)
保护继承忠,基类的公有成员和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可直接访问。
3.私有继承(private)
当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可直接访问。
三丶类型兼容性规则
1.派生类的对象可以隐含的转换为基类对象
class B{.....} class D:public B{} B b1,*pb1; D d1; b1=d1;
2.派生类的对象可以初始化基类的引用
class B{.....} class D:public B{} B b1,*pb1; D d1; B &rb=d1;
3.派生类的指针可以隐含转换为基类的指针
class B{.....} class D:public B{} B b1,*pb1; D d1; pb1=&d1;
四丶派生类的构造和析构函数
基类的对象必须调用基类的构造函数来进行初始化。所以,在派生类中,创建对象时,应该先调用基类构造函数,再调用自身构造函数进行初始化。
五丶多继承
当某类的部分或全部直接基类是从另一个共同基类派生而来,在这些直接基类中从上一层共同基类继承来的成员就拥有相同的名称。在派生类的对象中,这些同名数据成员在内存中同时拥有多个副本,同一个函数名会有多个映射。可以使用作用域分辨符来唯一标识并分别访问它们,也可以将共同基类设置为虚基类(virtual)。这时,从不同的路径继承过来的同名数据成员在内存中就只有一个副本,同一个函数名也只有一个映射。
class 派生类名:virtual 继承方式 基类名
六丶派生类的内存布局
派生类对象的内存布局需满足的要求是,一个基类指针,无论其指向基类对象,还是派生类对象,通过它来访问一个基类中定义的数据成员,都可以用相同的步骤。不同的编译器可以有不同的实现,这点要切记。
单继承情况:
class Base{。。。}; class Derived:public Base{ 。。}; Base *pba=new Base; Derived *pd=new Derived; Base *pbb=pd;
在Derived类的对象中,Derived从Base继承来的数据成员,全部放在前面,与这些数据成员在Base类的对象中放置的顺序保持一致,Derived类新增的数据成员全部放放在后面。
所以在pd赋pbb的过程中,指针不需要改变。pba和pbb这两个Base类型的指针,虽然指向的对象具有不同的类型,但任何一个Base数据成员到该对象首地址都具有相同的偏移量,因此,使用Base指针pba和pbb访问Base类中定义的数据成员时,可以采用相同的方式,而无须考虑具体的对象类型。
多继承情况:
class Base1{。。。}; class Base2{。。。。}; class Derived:public Base1,public Base2{ 。。}; Base1 *pbla=new Base1; Base2 *pb2a=new Base2; Derived *pd=new Derived; Base1 *pb1b=pd; Base2 *pb2b=pd;
将pd赋值给pd1b指针时,与单继承时的情形相似,只需要把地址复制一遍即可。但将pd赋值给pb2b指针时,不能简单执行地址复制操作,而应当在源地址的基础上加一个偏移量,使pb2b指针指向Derived对象中Base2类的成员的首地址。这样对于同为Base2类型指针的pb2a和pb2b来说,它们都指向Base2中定义的丶以相同方式分布的数据成员。
虚拟继承的情况:
class Base0{......}; class Base1:public Base0{......}; class Base2:public Base0{.......}; class Derived:public Base1,public Base2{.......}
Base1类型指针和Base2类型指针都可以指向Derived对象,而且通过这两类指针都可以访问Base0类中定义的数据成员,但这些数据成员在Derived对象中只有一份。一种比较理解的布局是:
在Base1类型对象和Base2类型对象中都增加一个隐含的指针,这个指针指向Base0中定义的数据成员的首地址。Derived类同时继承了Base1和Base2类,因此要把两个类中的隐含指针分别继承下来,但由于Derived类中的Base0类数据成员只有一份,因此Derived类型对象中的这两个隐含指针指向相同的地址。通过Base1类型指针和Base2类型指针访问Base0类的数据成员时,都可以通过指针来间接访问。