第8章 继承与派生
8-1. 教学目的与要求
1.理解继承的概念;
2.掌握继承的实现方法;
3.继承中常见问题的处理方法。
8-2. 主要内容及重点:
类是C++中进行数据封装的逻辑单位。继承是面向对象程序设计的一个重要特征之一,它允许在既有类的基础上创建新的类,新类可以从一个或多个既有类中继承操作和数据,而且可以重新定义或加进新的数据和操作,从而形成类的层次或等级。既有类称为基类或父类,在它基础上建立的新类称为派生类、导出类或子类。
本章的重点是派生类的定义和使用、创建派生类对象时构造函数的调用顺序、多重继承中的冲突及其消除方法以及作用域运算符的几种使用方法等。
本章的难点是基类的初始化、多重继承中的冲突以及虚基类等。
8-3. 第8章 继承-课件
3-4. 8-4. 第8章 继承-复习总结
继承是面向对象程序设计方法的基本特性之一,继承可以提高软件的重要性。
本章主要介绍了C++中的继承性的实现方法以及在继承性常见问题的处理方法。包括基类的初始化、访问类型的调整、冲突及其消除方法、成员覆盖、赋值兼容性以及虚基类等。
类的派生包括三种类型:私有派生、保护派生、公有派生。利用构造函数的初始化成员列表,可以在派生类中对基类成员进行初始化。在继承层次关系中要避免冲突的产生,通常是采用指定作用域和定义虚基类这两种方法来解决问题。
8-5. 第8章 继承-练习
8-5-1.思考题:
1).派生类有几种方式,每种方式中派生类对基类成员的继承如何?
2). 在创建派生类对象时,构造函数的执行次序是怎样的?析构函数的执行次序是怎样的?
3). 派生类对象如何对基类中的成员进行初始化?
4). 在多重继承中,在什么情况下会产生冲突?如何消除冲突?
5). 列举我们已学习的作用域运算符“::”的所有用途。
6). 属于不同类的对象在什么情况下可以互相赋值?
7).什么叫虚基类?为什么要引进虚基类?
8-5-2.练习题:
课后练习题见练习单元。
第8章 继承与派生
类是C++中进行数据封装的逻辑单位。继承是面向对象程序设计的一个重要特征之一,它允许在既有类的基础上创建新的类,新类可以从一个或多个既有类中继承操作和数据,而且可以重新定义或加进新的数据和操作,从而形成类的层次或等级。既有类称为基类或父类,在它基础上建立的新类称为派生类、导出类或子类。
8 .5 虚基类
8 .5.1 虚基类的概念
对于直接继承,一个基类只能被派生类继承一次;
对于间接继承,一个基类可以被派生类继承多次。
如图所示的继承结构中,基类A在类D中会产生两个副本。这时如果在D类中使用从A类继承的成员时,如果不指定其作用域,会出现同名冲突。
虚基类的基本思想是:将一个基类声明为虚基类时,不管它在派生类中被继承多少次,该基类中的成员在该派生类中始终只有一个副本,如图。
类D
类C
类B
类A
图 虚基类的类层次
虚基类是通过关键字virtual实现的,定义虚基类的格式为:
class <派生类>:virtual <access><基类名>《,---,virtual<accesss<基类名>>》
{ --- } ;
或
class<派生类名>:<access>virtual <基类名>《,---,<access>virtual<基类名>》
{---} ;
例1:使用虚基类,使派生类中只有基类一个副本。
# include <iostream.h>
class CFurniture
{ protected:
int weight ;
public:
CFurniture( ){ }
void SetWeight (int i ) { weight=i ;}
int GetWeight ( ) {return weight ;}
};
{cout<<”sleeping ---\n’ ;}
class CBed:virtual public CFurniture //A 定义虚基类
{public :
CBed ( ){ }
void sleep ( )
} ;
class CSofa:virtual public CFurniture //B 定义虚基类
{ public:
CSofa ( ) { }
void WatchTV()
{cout<<”Watch TV . \n “ ;}
} ;
class CSleepSofa :public CBad ,public Csofa
{public :
CSleepSofa ( ){ }
void FoldOut ( )
{cout<<”Fold out the sofa 。 \n ” ;}
};
void main ()
{CSleepSofa ss ;
ss.SetWeight (20) ; //C
cout<<ss.GetWeight( )<<’\n’ ; //D
}
执行结果:
20
例 //program 8-6.cpp p269
#include <iostream.h>
class A {
public:
int i;
void showa(){cout<<"i="<<i<<endl;}
};
class B: virtual public A //对类A进行了虚拟继承
{
public:
int j;
};
class C: virtual public A //对类A进行了虚拟继承
{
public:
int k;
};
class D : public B, public C
//派生类D的二基类B、C具有共同的基类A,但采用了虚拟继承
//从而使类D的对象中只包含着类A的1个实例
{
public:
int n;
void showall(){cout<<"i,j,k,n="<<i<< “ ,”<<j<< “ ,”<<k<< “ ,”<<n<<endl;}
};
void main() {
D Dobj; //说明D类对象
Dobj.i=11;
//若非虚拟继承时会出错!
// -- 因为“D::a”具有二义性
Dobj.j=22;
Dobj.k=33;
Dobj.n =44;
Dobj.showa();
//若非虚拟继承时会出错!
// -- 因为“D::showa”具有二义性
Dobj.showall();
}
程序执行后的显示结果如下:
/*
i=11
i,j,k,n=11 ,22 ,33 ,44
Press any key to continue
*/
8 .5.2 虚基类的初始化
虚基类的初始化与一般的多重继承的初始化在语法上是一致的,但构造函数的调用次序有不同。
1.虚基类的构造函数在非虚基类的构造函数之前调用。如下例的B行
2.若同一层次中包含多个虚基类,这些虚基类的构造函数按它们的说明次序调用。
3.若虚基类由非虚基类派生而来,则仍然先调用基类的构造函数,再调用派生类的构造函数,如下例的A行。
类D
<P style="MARGIN: