c++继承与派生详解

继承是面向对象程序设计中的重要特性,是软件复用的一种形式。其实继承和派生就是同一种概念的不同说法罢了,通常我们说子类继承父类,父类派生出子类。说实话,学习和了解了C++的继承与派生后,发现我对面向对象中的继承与派生有了更深刻的理解。在更加深入了解C++关于继承方面的知识之前,我想先讲一下C++中派生与继承的基本概念,它是之后学习派生类的构造与析构,以及多重继承等的基础。

  1.派生类的声明

  声明一个派生类的一般格式:

  class 派生类名:[继承方式] 基类名

  {

     派生类新增的数据成员和成员函数

  };

  对于上述的格式主要想说一下继承方式的作用,它主要是规定如何访问从基类继承的成员。它可以有关键字private、protected和public来分别表示私有继承、保护继承和公有继承。如果不显示继承方式关键字,系统默认为私有继承。继承方式指定类派生类成员以及类外对象对于从基类继承来的成员的访问权限。

  2.对于不同的继承方式,基类成员在派生类中的访问属性也是不同的,以下表,列出了基类成员在派生类中的访问属性(我想以一张表格的方式来说明,既显而易见,又简单易懂):

  3.最后说一下,派生类对基类成员的访问规则,无非访问方式就两种:内部访问和对象访问。同样的3中继承方式下,派生类对基类成员的访问也分为3类。对于3种访问规则,我将用实例和小结一一对应:

(1).私有继承的访问规则

示例:

复制代码
View Code
复制代码

结果:

由以上示例可小结

私有继承那么派生类继承下来的基类中的成员,在派生类中就都成为了私有成员,因此,派生类的对象访问任何基类中的成员(数据成员a1,a2,a3和成员函数showA())都不允许,而派生类内部不可访问基类中的私有成员(a1),可访问保护成员(a2)和公有成员(数据成员a3,成员函数showA());

(2).保护继承的访问规则

对上述示例代码中的private继承换为protected

复制代码
View Code
复制代码

由以上示例可小结(运行结果和上一示例相同)

保护继承那么派生类继承下来的基类中的成员,除了私有的基类成员不变外,在派生类中就都成为了保护成员,因此,派生类内部不可访问基类中的私有成员(a1),可访问保护成员(a2)和公有成员(数据成员a3,成员函数showA());而派生类的对象访问任何基类中的成员(数据成员a1,a2,a3和成员函数showA())都不允许,a1是私有成员,不能访问,但a2,a3和showA()被继承下来在派生类中是保护成员,它们不能访问是因为protected保护类型的成员可以被本类的成员函数访问,也可以被继承被类的的派生类成员函数访问,但类以外的任何访问都不允许,即它为半隐蔽的。

(3).公有继承的访问规则

示例:

复制代码
View Code
复制代码

结果:

由以上示例可小结

公有继承那么派生类继承下来的基类中的成员,除了私有的基类成员和保护的基类成员不变外,在派生类中就都成为了公有成员,因此,派生类内部不可访问基类中的私有成员(a1),可访问保护成员(a2)和公有成员(数据成员a3,成员函数showA());而派生类的对象可以访问基类中数据成员a3和成员函数showA(),但不允许访问私有成员a1和保护成员a2。

  4.最后总结一下,其实这节所讲的内容归根到底就是对于继承方式的理解,以及从继承方式所引出的访问属性和规则的相对应的不同区别。因为觉得直接用文字来表达,看起来会比较乱,所以试着参入表格和示例小结来说明。希望这样能达到读者在看这篇随笔时不会觉得思绪很混乱!


上一节,主要讲解了有关派生类继承方式的内容。那么今天就来说说派生类的构造函数和析构函数,以及怎么样在派生类中显式访问积累成员。大家都知道,基类的构造函数和析构函数是不能被继承的,因此我们必须在派生类的构造函数中对基类的构造函数所需要的参数进行设置。同样,对于派生类对象的清理工作也需要加入新的析构函数。

  1.那么该如何构造呢?对于简单的派生类,即只有一个基类,且直接派生(多继承将在后续几节中做详细讲解),来讲,如果基类的构造函数没有参数,或者没有显式定义构造函数,那么派生类可以不向基类传递参数,甚至可以不定义构造函数。但是一旦基类含有带参数的构造函数时,派生类必须定义构造函数,并把参数传递给基类构造函数。其一般格式:

  派生类名(参数总表)::基类名(参数子表)

  {

    派生类新增成员的初始化语句;

  }

而析构函数可以有用户自己定义,由于其是不带参数的,所以在派生类中是否要自定义析构函数与它所属基类的析构函数无关。在执行派生类的构造函数时,系统会自动调用基类的析构函数,进行对象清理。下面举例来说明一下简单派生类的构造和析构: 

复制代码
View Code
复制代码

结果:

在结果里我们可以看到派生类构造函数和析构函数的调用顺序,当创建派生类对象时,首先调用基类的构造函数,再调用派生类构造函数,而当清理对象时,则刚好相反。

  那么如果当派生类中存在成员对象,那么构造函数和析构函数的调用顺序又如何呢?在解答这个问题之前,我们先看一下当派生类中含有子对象时,构造函数的一般格式:

  派生类名(参数总表):基类名(参数子表),对象名1(参数子表1),对象名2(参数子表2)

  {

    派生类新增成员的初始化语句;

  } 

还是,我们用一个示例来说明,并通过实际的运行结果来解答上述的问题:

复制代码
View Code
复制代码

结果:

从结果中很清楚的说明了其构造函数和析构函数的调用顺序。另外读者也可以在类D成员对象c后面再声明一个基类对象,运行看看,修改过后其构造和析构的调用顺序又如何?最后一点要补充的是,如果派生类的基类也是派生类,那么每个派生类只需要负责其直接的基类数据成员的初始化

  2.在定义派生类时,C++里是允许派生类中的成员名和基类中的成员名相同,出现这种情况,我们称派生类成员覆盖了基类中使用相同名称的成员。也就是说当你在派生类中或用对象访问该同名成员时,你所访问只是派生类中的成员,基类中的就自动被忽略。但如果我确实要访问到基类中的同名成员那怎么办,C++中这样规定必须在成员名前加上基类名和作用域标识符"::"。

  3.最后还是一样,我将用一个示例来总结一下今天所讲的内容(开发工具:vs2010):

复制代码
View Code
复制代码

结果:

大家都知道C#和C++重要的区别之一就是,C#支持单一继承和多接口继承,而C++则允许多继承。至于哪一个比较好,在这里就不去评价了。今天所要说的重点内容就是C++的多重继承以及虚基类。

  1.前一节我介绍了有关单继承的内容,实际上,一个派生类可能会继承于两个或多了基类,那么这样的派生方法称为多基派生或多重继承。它声明的一般格式:

  class 派生类名:继承方式1 基类名1, 继承方式2 基类名2, 继承方式3 基类名3, ...,继承方式n 基类名n

  {

    派生类新增的数据成员和成员函数;

  }

在多重继承中,公有继承、保护继承以及私有继承对于基类成员在派生类中的访问属性与单继承的规则是相同的。同样的多重继承派生类的构造函数和析构函数又是如何定义呢?一般形式:

  派生类名(参数总表):基类名1(参数子表1), 基类名2(参数子表2),...,基类名n(参数子表n)

  {

    派生类新增成员的初始化语句;

  }

与单继承派生类构造函数一样,多继承下的派生类构造函数必须同时负责该派生类的所有基类构造函数的参数传递。且构造函数和析构函数的调用顺序和单继承是一样的(可参考C++之继承与派生(2) ),先调用基类构造函数,再调用对象成员的构造函数,最后调用派生类的构造函数。那么处于同一层次的各个基类构造函数的调用顺序是取决于声明派生类时所指定的各个基类的顺序。我们通过以下这个示例说明:

(1).组件基类

复制代码
View Code
复制代码

(2)窗体类

复制代码
View Code
复制代码

(2)滚动条类

复制代码
View Code
复制代码

(4)带有滚动条的窗体类

复制代码
View Code
复制代码

(5)main主函数实现部分

复制代码
View Code
复制代码

结果:

从结果看也验证了之前所说的多重继承派生类中处于同一层次的各个基类构造和析构的调用顺序。

  2.看了上一个示例代码,也许有读者会问,为什么在ScrollBarWindow类中,有那么多和ComponentBase类同名的成员,既然ScrollBarWindow继承ScrollBar类和Window类,而ScrollBar类和Window类又继承ComponentBase类,那ScrollBarWindow类应该就拥有ComponentBase类中的这些同名成员了,为什么还要在重新声明,可以把这些同名的成员全部删掉,在main()主函数里声明对象直接调用这些同名的成员函数不就OK了么,这样重复声明不就完全失去继承的所体现的代码复用的特点了吗?读者可以尝试一下,你会发现会提示有关于成员XX不明确的错误,即产生了二义性,为什么会出现这个问题呢?单继承是不会有出现这样的问题的,原因在多重继承上。

  上述程序代码中,类ScrollBarWindow由ScrollBar类和Window类派生出来,而ScrollBar类和Window类由ComponentBase类派生出来,虽然ScrollBar类和Window类没有定义数据成员name,但是它们分别从ConponentBase类继承了数据成员name,这样ScrollBar类和Window类就同时存在数据成员name,虽然ScrollBar类和Window类中的数据成员name具有不同的存储单元,可以存放不同数据。但是派生出的类ScrollBarWindow中就有两份数据成员name,其他同名成员也是一个道理,这是如果我们在直接声明类ScrollBarWindow的对象调用这些同名成员函数,那就会出现上述所说的二义性的问题。为了解决这个问题,C++中引入了虚基类的概念。

  虚基类声明的一般格式:

  class 派生类名:virtual 继承方式 类名{}//或class 派生类名:继承方式 virtual 类名{}

现在我们对上一个示例代码做一次瘦身:

(1).组件基类不变

(2).窗体类

复制代码
View Code
复制代码

(3).滚动条类

复制代码
View Code
复制代码

(4).带有滚动条的窗体类

复制代码
View Code
复制代码

(5).main()主函数实现部分

复制代码
View Code
复制代码

结果:

发现结果和上一个示例一样,但是类ScrollBarWindow中的代码却大大减少了,从该示例中可以看到虚基类在多重继承中所体现的重要性。

  其实把ComponentBase声明为类ScrollBar和类Window的虚基类后,从类ScrollBar和类Window派生出来的类ScrollBarWindow只继承了类ComponentBase一次。最后对于虚基类我想补充几个注意点:(1)如果在虚基类中定义了带有参数的构造函数,且没有定义默认形式的构造函数,则在整个继承过程中,所有直接或间接的派生类都必须在构造函数的成员初始化表中列出对虚基类构造函数的调用;(2)建立一个对象时,如果这个对象中含有从虚基类继承来的成员,则虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的,该派生类的其他基类对虚基类的调用构造函数则被忽略;(3)若在同一层次中同时包含虚基类和非虚基类,那么先调用虚基类的构造函数,在调用非虚基类的构造函数,最后调用派生类的构造函数,析构则相反;(4)对于多个虚基类,则构造函数执行顺序从左到右;(5)对于多个非虚基类来说,构造函数的执行顺序也是如此;(6)若虚基类由非虚基类派生而来,那么仍然先调用基类构造函数,再调用派生类的构造函数;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值