继承与派生
继承:一旦指定了某种事物的父代的本质特征,那么它的子代将会自动具有那些性质,这就是一种朴素的可重用的概念。
派生: 子代可以拥有父代没有的特性,这就是可扩充的概念
继承就是在已经存在的基础上建立一个新的类,已存在的类称为基类或父类, 新建立的类称为派生类或子类。
派生类的功能:
1、吸收基类成员
2、改造基类成员
3、添加新成员
单继承和多继承
单继承: 派生类只有一个直接基类。
语法格式:class 派生类名:<继承方式> 基类名{
…//派生类修改基类成员
…//派生类添加新成员
};
由继承方式的不同而导致的继承成员访问属性的改变
基类: 公有成员 私有成员 保护成员
公有派生类:公有成员 不可访问成员 保护成员
私有派生类:私有成员 不可访问成员 私有成员
保护派生类:保护成员 不可访问成员 保护成员
不可访问成员 :在类外不能被直接访问,在派生类的类内不能被直接访问。
派生类与基类同名成员的访问方式:
c++允许派生类可以重新定义基类成员,此时称派生类成员覆盖了基类的同名成员。
如果在派生类中,想使用基类的同名成员,则可以显式地使用用类名+限定符的方式:
基类名::成员名
单继承派生类的构造函数
派生类构造函数(参数表):基类构造函数(参数表),对象成员1(参数表),…对象成员函数n(参数表),{…//初始化自定义数据成员}
class Circle{
Point center;
float radius;
......
public
Circle(float x,float y,float r):center(x,y)
{
radius=r;
}
}
class ColorCircle:public Circle{
int color;
public:
ColorCircle(float x,float y,float r,int color):Circle(x,y,z){
this->color=color;
}
如果基类使用的是缺省的构造函数或不带参数的构造函数,那么在初始化列表中可以省略:基类构造函数这已项,对象成员参数列表同理。
构造函数和析构函数的调用顺序:
构造函数: 1、调用基类构造函数 2、再调用对象成员所属类的构造函数 3、最后调用派生类构造函数;
析构函数:1、先调用派生类析构函数 2、再调用对象成员析构函数 3、最后调用基类构造函数。恰好与构造函数的调用相反。
class Base{
public:
Base(){cout<<"Base obj created"<<endl;}
~Base(){cout<<"Base obj deleted"<<endl;}
};
class Derived:public Base{
public:
Derived(){cout<<"Derived obj created"<<endl;}
~Derived(){cout<<"Dericed obj deleted"<<endl;}
};
int main(){
Derived d;
return 0;
//则其结果的输出是什么呢?先调用了基类的构造函数,输出Base obj created;
//再调用对象成员函数,此处没有,然后调用派生类构造函数输出:Derived obj created;
//之后调用析构函数,实现顺序与之相反,因此会输出:Derived obj deleted Base obj deleted;
多继承 派生类有多个直接基类
定义多继承派生类语法格式
class 派生类名 :<继承方式>基类名1,<继承方式>基类名2,…
{
…//派生类新添加的成员
};
多继承派生类构造函数,和析构函数的执行顺序与单继承派生类一致。
多继承存在一个问题,当访问不同基类的同名成员时,会存在二义性。
解决办法:用类名对成员进行加以限定,列如:
C1.A::f();或者C1.B::f();其中C1时、是派生类名,A和B则是基类名,::命名空间,f()是基类中的成员。
访问共同基类成员的二义性:当一个派生类对象是由两个基类成员派生而来。并且两个基类成元是由同一个派生类对象派生来时,此时如果我们想要访问最初的那个基类函数的成员1时,也会出现二义。
解决办法: Cobj.B1::a=9或Cobj.B2::a=9;
其中Cobj是派生类对象,B1是派生类的基类对象,a是B1内多成员函数。其实质访问的是派生类的基类的基类内部成员。
虚基类
类A是派生类D两条继承路径上的一个公共基类,
因此这个公共基类会在派生类对象中产生两个基类子对象,虽然可以通过限定符的方式避免二义性,但如果我们不需要在派生类对象中存在多个基类对象的拷贝。因此如果要使在公共基类在派生类中只产生一个基类子对象,则需要将这个基类设置为虚基类。+virtual。
class base{...};
class base2{...};
class level1:public base2,virtual public base{...};
class level2:public base2,virtual public base{...};
class toplevel:public level1,virtual public level2{...};
//当声明toplevel类的对象时,构造函数的调用次序为:
//base base2 level2 base2 level1 toplevel;
//虚构函数首先调用,并且只调用一次。