首页 >> 大全

2.继承结构

2024-01-03 大全 35 作者:考证青年

继承是面向对象编程语言当中,最重要的部分,也是代码重用的一种重要形式。

1.基本形式

首先继承的有三种基本形式,分别是、、,代表公有继承、私有继承和保护继承,之前在介绍作用范围的时候提过这三者的区别,但这并不重要。就以目前我个人遇到过的代码来说,99%的情况下,全部使用的都是公有继承(),另外两种极少用到,建议大家不用关注,遇到了再查找资料。

继承的基本写法如下,在子类/派生类后面跟上":",再加上继承形式,最后是被继承的父类/基类。

class Derivation : public Base
class Child : public Father

父类、子类、基类、派生类,这几个概念看起来比较复杂,其实关系就像上面的代码所显示那样,已经存在的类叫做基类/父类,继承后的类叫子类/派生类。通常我们画类图的时候,都是从子类/派生类指向基类/父类。

那么继承意味着什么?

以最最常规的情况来说,继承实际就相当于原封不动的复制了一份父类/基类的信息(包括函数和数据)到子类/派生类当中,就像孩子继承父亲的财产。例如上面的情况,父类/基类当中存在和dara1,如果使用了继承,那么子类/派生类就相当于默认在当中添加了和data1,且它们的权限和父类/基类一模一样(对于继承来说)。

在代码当中,对于A当中的元素,用同样的方法在B当中也可以使用。

class A {
public:int m_a;
};class B :public A {
//public://int m_a;    //由于继承,这部分内容是隐含的
};int main() {B b;b.m_a = 1;cout << "b.m_a = " << b.m_a << endl;return 0;
}

显然,通过这样一种方式来处理那些重复性的函数和数据,可以极大的减少我们的工作量,因为类B当中可以包含A的全部信息。否则对于B类,我们需要在它当中手动添加这些重复性的函数和数据,这就是继承对于代码重用性的体现。

不过继承也不能滥用,当出现B is A即B是A的时候,一般可以使用继承。

需要注意的是,继承所带来的函数和数据,对于代码编辑器来说,不是直接写在当前类里面的。也就意味着,查看代码时,如果出现了继承,我们就需要去到基类/父类当中查看当前类所含有的额外信息。

2.继承结构

上面只是继承的基本结构,它只代表了类A和B之间的关系,实际使用时要复杂得多。将多个类继承进行嵌套和组合使用,这样就有了多级继承和多重继承。

2.1.多级继承

像愚公移山那样,子又生孙,孙又生子,子又有子,子又有孙,子子孙孙无穷匮也。继承也可以形成一条链路,B继承自A,C继承自B,D继承自C。。。

class A {
};class B :public A {
};class C :public B{
};

其中B包含A的全部信息,C又包含B的全部信息,因此C其实包含了A和B的全部信息,以此类推,每一次继承,不管这条链路有多长,子类/派生类都可以获得链路上面所有父类/基类的信息,这就是多级继承。

2.2.多重继承和单一继承

虽然在生物学上只能有一个父亲,但在“财产继承”上却不是,没人规定我们只能继承一个先祖的财产。所以就会出现,单个类继承多个父类./基类的情况,这就被称为多重继承。在程序当中使用逗号","隔开这些父类/派生类。

class A {
};class B {
};class C :public B,public A{
};

一个类继承自多个父类/派生类,那么自然有另外一种情况,一个类继承给多个子类/基类,就像是一个父亲有多个孩子那样。

这没有特殊的名称,是单一继承。所谓的单一和多重都是基于子类/派生类来说的。

class A {
};class B : public A{
};class C : public A{
};

2.3.继承链路

基于上述的不同继承结构,在实际使用当中,都是组合使用,以此构建出复杂且庞大的继承链路结构。

3.元素重名 3.1.单一类被多次继承

假如我在设计时不小心故意地造成了以下情况:

B继承了A,且C又继承了A和B,那对于C来说,A就被继承了两次。

那么问题来了,如果还是按照基本形式来理解,即将A的内容在B当中重新写一遍,那么并不合理,因为这样会出现一模一样的元素,相当于C当中有两份一模一样的内容来自A。

这种情况的确是被允许的,A的内容会因此被拷贝为两份,对于C来说就相当于无中生有,凭空多出来了一份内容。但是情况就没有那么简单了,在实际访问时没有办法像常规的继承那样直接访问,因为A无法确定继承的内容是继承自A还是来通过B继承自A的。

需要在访问时使用","指定继承的来源,再使用"::"访问对应成员。这一用法对于一般情况也适用,但是并没有太大意义。

class A {
public:int m_a;
};class B :public A {};class C : public B, public A {
public:C() {} ;
};int main() {C c;//c.m_a; //错误,无法确定元素来源c.A::m_a = 1;c.B::m_a = 2;c.B::A::m_a = 3;cout << "c.A::m_a = " << c.A::m_a << endl;    // 输出1cout << "c.B::m_a = " << c.B::m_a << endl;    // 输出2cout << "c.B::A::m_a = " << c.B::A::m_a << endl;    // 输出3return 0;
}

在上面的例子当中,如果定位到A的内容是来自B时,就已经确认了是来自B继承的A。因此c.B::m_a和c.B::A::m_a所表示的内容是一样的,

3.2.重名

假如我在设计时故意不小心让子类/派生类和父类/基类当中的元素名字一样,那又会怎么样?

class A {
public:int m_a;
};class B :public A {
public:int m_a;
};int main() {B b;b.m_a = 1;b.A::m_a = 2;cout << "b.m_a = " << b.m_a << endl;    // 输出1cout << "b.A::m_a = " << b.A::m_a << endl;    // 输出2return 0;
}

这个时候,同样可以保存两份内容,直接进行访问就是自己的元素,指名的话就是对应的父类/基类的元素。

但是,必须要强调。原则上,我们不应当这种出现元素重名的情况,这是写代码当中应当避免的,这对于代码的编写和维护都是灾难,如果出现这种情况,最好想办法重新调整下名字或者架构。

4.补充

其它一些性质作为补充:

4.1.友元关系无法继承 4.2.对于静态成员来说,无论如何继承,整条链路上始终只能存在一个 4.3.子类/派生类的前向声明不需要加上继承的元素

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了