`
saybody
  • 浏览: 869401 次
  • 性别: Icon_minigender_2
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

C++学习摘要之三:继承和派生

阅读更多

第三章继承和派生

继承性是面向对象程序设计最重要的机制之一。

1.继承的概念

所谓继承(inheritance)就是利用已有的数据类型定义出新的数据类型。
在继承关系中,被继承的类称为基类(base class)或父类,而通过继承关系定义出来的新类则被称为派生类(derived class)或子类。
派生类既可以对基类的性质进行扩展又可以对基类进行限制,从而得到更加灵活、适用的可重用模块,大大缩短了程序的开发时间。
一个派生类既可以从一个基类派生也可以从多个基类派生。从一个基类派生称为单继承;从多个基类派生称为多重继承。
(1)单继承
单继承的定义形式如下:
class 派生类名:访问方式 基类名
{
派生类中的新成员
};
其中,派生类名是新定义的类名。基类名必须是程序中已有的一个类。
在单继承中,每个类可以有多个派生类,但是每个派生类只能有一个基类。
(2)多重继承
所谓多重继承是指派生类从多个基类中派生而来的。
定义多重继承类的方式如下:
class 派生类名:访问方式 基类名1,访问方式 基类名2……
{
派生类中的新成员
};
从定义格式上来看,多重继承与单继承的区别主要是多重继承的基类多于一个。
(3)访问方式
不管在单继承还是在多重继承的定义格式中,访问方式,即继承方式,可以为public、private或protected,如果省略,则默认为private方式。
访问方式为public方式时,这种继承称为公有继承;
访问方式为private方式时,这种继承称为私有继承;
访问方式为protected方式时,这种继承称为保护继承。

2.派生类的生成过程

在给出了派生类的定义和相应成员函数的实现代码后,整个派生类的定义就算完成了,这是就可以利用该类定义相应的对象处理实际问题了。
由于派生类是在基类的基础上经过继承而产生的,所以搞清派生类中到底有哪些成员对于更好的使用派生类是很重要的。事实上,派生新类经历了三个步骤:
(1)吸收基类成员
派生类继承吸收了基类的全部数据成员以及除了构造函数、析构函数之外的全部函数成员。也就是说,基类中的构造函数和析构函数使不能继承到派生类中的。
(2)改造基类成员
对继承到派生类中基类成员的改造包括两个方面:一是基类成员的访问方式问题,这由派生类定义时的访问方式来控制;二是对基类数据成员或成员函数的覆盖,也就是在派生类中定义了与基类中同名的数据成员或函数成员,由于作用域不同,于是发生同名覆盖,基类中的成员就被替换成派生类中的同名成员。
(3)添加新成员
在派生类中,除了从基类中继承过来的成员外,还可以根据需要在派生类中添加新的数据成员和成员函数,以此实现必要的新功能。可以看出,在派生类中可以添加新成员的机制是继承和派生机制的核心,保证了派生类在功能上比基类有所发展。

3.继承方式对基类成员的访问控制

前面已经分析,派生类继承和吸收了基类的全部数据成员和除了构造函数、析构函数之外的全部函数成员,但这些成员在派生类中的访问属性是可以调整的,这是由派生类定义格式中的继承方式来决定的,也就是继承方式控制了基类中具有不同访问属性的成员在派生类中的访问属性。
由于继承方式可以有public、private和protected三种,不同的继承方式会导致原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同。这种访问包括两个方面:一是派生类中新增成员对从基类继承来的成员的访问;二是派生类的外部通过派生类的对象从基类继承来的成员的访问。
(1)公有继承
当类的继承方式为公有继承时,基类中public和protected成员的访问属性在派生类中不变,而基类private成员不可访问。也就是说,基类的public和protected成员在公有继承方式下分别继承为派生类的public和protected成员,派生类中的其他成员可以直接访问它们,在派生类的外部只能通过派生类的对象访问从基类继承来的public成员。而无论是派生类的成员还是派生类的对象都无法访问从基类继承来的private成员。
(2)私有继承
当类的继承方式为私有继承时,基类中的public和protected成员都以private成员出现在派生类中,而基类private成员不可访问。也就是说,基类的public和protected成员在私有继承方式下被继承为派生类的private成员,派生类中的其他成员可以直接访问它们,但在派生类的外部无法通过派生类的对象访问它们。而无论是派生类的成员还是派生类的对象都无法访问从基类继承来的private成员。
可以看出,经过私有继承后,所有基类的成员都成为派生类的私有成员,如果进一步派生的话,基类的成员就无法在新的派生类中被访问。因此,经过私有继承后,基类的成员再也无法在以后的派生类中发挥作用,实际是相当于中止了基类功能的继续派生。
(3)保护方式
当类的继承方式为保护继承时,基类中的public和protected成员都以protected成员出现在派生类中,而基类private成员不可访问。也就是说,基类的public和protected成员在保护继承方式下被继承为派生类的protected成员,派生类中的其他成员可以直接访问它们,但在派生类的外部无法通过派生类的对象访问它们。而无论是派生类的成员还是派生类的对象都无法访问从基类继承来的private成员。

4.派生类的构造函数和析构函数

继承和派生的机制可以使派生类继承基类的成员,从而实现了原有代码的重用,但是,由于基类的构造函数和析构函数不能继承,那么在派生类中,如果对派生类新增的成员进行初始化,就必须在派生类中根据需要加入新的构造函数,如果对从基类继承下来的成员进行初始化,还必须由基类的构造函数来完成,所以需要在派生类中的构造函数,一方面负责调用基类的构造函数对基类成员进行初始化,另一方面还要负责对基类的构造函数所需要的参数进行必要的设置。
(1)单继承方式下派生类构造函数的定义
在单继承方式下,派生类的构造函数的定义格式如下:
派生类名::派生类构造函数名(形参表):基类构造函数名(参数表)
{
//派生类构造函数的函数体
}
在此定义格式中,派生类构造函数名后面括号内的参数表中包括参数的类型和参数名,而基类构造函数名后面括号内的参数表中只有参数名而没有参数类型,并且这些参数必须是来源于派生类构造函数名后面括号内的参数。
(2)多重继承方式下的派生类构造函数的定义
在多重继承方式下,派生类的构造函数必须同时负责所有基类构造函数的调用,对于派生类构造函数的参数个数必须同时满足多个基类初始化的需要。所以,在多重继承方式下,派生类的构造函数的定义格式如下:
派生类名::派生类构造函数名(参数表):基类名1(参数表1)基类名2(参数表2)……
{
//派生类构造函数的函数体
}
其中,第1个参数表中的参数包含了其后的各个参数表中的参数。
(3)派生类构造函数的执行次序
派生类构造函数执行的一般次序为:
1)调用基类构造函数,调用顺序按照它们被继承时说明的顺序(从左到右),而不是按派生类构造函数在初始化表中的次序;
2)调用子对象的构造函数(如果在派生类中存在子对象的话),调用顺序按照它们在类中说明的顺序;
3)执行派生类构造函数的函数题。
当派生类的对象被删除时,派生类的析构函数被执行。由于基类的析构函数不能被继承,因此在执行派生类的析构函数时,基类的析构函数也将被调用。而执行顺序是先执行派生类的析构函数,再执行基类的析构函数,其顺序与执行构造函数是的顺序正好相反。

5.虚基类

在多重继承的情况下,派生类具有两个以上的直接基类,而这些直接基类的一部分或全部又是从另一个共同基类派生而来的,这些直接基类中从上一级基类继承来的成员拥有相同的名称,在派生类的对象中,这些同名成员在内存中同时拥有多个拷贝,如何进行分辨呢?有两种方法,一是使用作用域运算符唯一标帜并分别访问它们;二是将直接基类的共同你基类设置为虚基类。
(1)使用作用域运算符方法
这种方法就是在需要访问的成员名前加上直接基类名和作用域运算符“::“。其格式是:
直接基类名::数据成员名
直接基类名:成员函数名(参数表)
(2)虚基类的方法
该方法就是将直接基类的共同基类设置为虚基类,即在基类的访问方式前加上关键字“virtual“,声明虚基类的格式如下:
class 派生类名:virtual 访问方式 基类名
{//声明派生类成员};
虚基类虽然被一个派生类间接地多次继承,但派生类却只继承一份该基类的成员,这样就避免了在派生类中访问这些成员时的二义性。

6.虚基类机制下的构造函数的执行顺序

虚基类机制下的构造函数的执行顺序与一般多重继承下的构造函数的执行顺序是不同的,其执行顺序如下:
(1)一个类的所有直接基类中,虚基类的构造函数在非虚基类之前调用;
(2)如果一个类的所有直接基类中有多个虚基类,则这些虚基类的构造函数的执行顺序与在派生类中的说明的次序相同;
(3)若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再按照派生类中构造函数的执行顺序调用。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics