一、什么是继承?
百度百科:利用已有的数据类型来定义新的数据类型
通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员。我们称已存在的用来派生新类的类为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。
一般形式
class 子类名:继承方式 父类名
二、继承中的权限变化
继承父类中的数据,以不同的方式继承会改变父类原有数据在子类中的权限。只改变数据在子类中的权限,不改变父类原有权限。
- 以public的方式继承
class ChildClass:public FatherClass
不改变权限。父类中private下的数据继承到子类中也是private,protected和public下的也一样。
- 以protected继承
class ChildClass:protected FatherClass
只改变父类中的public数据权限。将父类中public权限的数据变成protected后继承到子类中,其它数据不变。
- 以private继承
class ChildClass:private FatherClass
所有数据都以private的方式继承。子类中所有在父类中继承的数据都改为private权限。
三、继承中的构造函数
子类需要调用父类构造函数初始化数据(或者父类中有无参构造则不用)。
class FatherClass
{
public:
FatherClass(int num) { m_Fnum = num; }
int m_Fnum;
};
class ChildFather :public FatherClass
{
public:
ChildFather(int n) :FatherClass(n) {} //用n初始化继承下来的m_Fnum
};
四、继承的其它形式
- 多继承
大多数使用继承都是单继承(上面的例子就是单继承),还有多继承。
多继承:两个以上的父类
class ChildClass:public FatherClass1, FatherClass2
{
public:
ChildClass(int num) :FatherClass1(num),FatherClass2(num) {}
};
多个继承用","隔开。
- 菱形继承
为了解决从两个父类汇总继承相同属性的二义性。
比如,FatherClass1中有个属性m_Fnum,FatherClass2中也刚好有个同名的m_Fnum(一般是两个父类继承了相同的类造成的),那么,ChildClass在使用这个属性时就会不明确。
class GrandFatherClass //祖父类
{
public:
GrandFatherClass(int Gnum) { m_Gnum = Gnum; }
int m_Gnum;
};
class FatherClass1:virtual public GrandFatherClass //父类,虚继承
{
public:
FatherClass1(int Fnum1):GrandFatherClass(Fnum1) {}
};
class FatherClass2 :virtual public GrandFatherClass //父类,虚继承
{
public:
FatherClass2(int Fnum2) :GrandFatherClass(Fnum2) {}
};
class ChildClass:public FatherClass1, FatherClass2 //子类
{
public: //所有继承的构造函数都要调用
ChildClass(int num) :GrandFatherClass(num),FatherClass1(num), FatherClass2(num){}
};
FatherClass1和FatherClass2 继承GrandFatherClass类,ChildClass 又继承FatherClass1和FatherClass2类,就像一个菱形一样。
例子中,本来FatherClass1, FatherClass2都继承了GrandFatherClass,只有一份m_Gnum,ChildClass 又分别继承了FatherClass1, FatherClass2,应该有两份m_Gnum产生二义性,但是FatherClass1, FatherClass2使用的是virtual虚继承,并不会像原来那样把两份直接继承下来,而是只留一份,也可以看成是从GrandFatherClass中继承下来的。
最后数据的值,由参数列表中GrandFatherClass的初始化值决定。
注意:要调用间接继承的GrandFatherClass类的构造函数,同时也要调用直接继承的两个类的构造函数。
五、其它
-
同名问题(二义性)
就近原则。当子类中出现和父类同名的属性时,子类创建的对象使用子类中的属性,父类创建的对象使用父类中的属性。 -
指针访问
ChildClass* pC = new ChildClass(1);
- 上行转换
用子类初始化父类,因为子类调用了父类的构造函数,实际上是可行的有初始化数据的。
FatherClass1* pB = new ChildClass(1);
因为是FatherClass1类,使用的是FatherClass1中的数据,没有子类的属性。
4.下行转换、交叉转换
一般情况下不被允许,强行使用需要static_cast、dynamic_cast等手段转。