第 10章 继承与派生
10.1 单一继承
10.2 多重继承
10.3 虚基类
10.1 单一继承
C++的重要性能之一是代码重用。为了达到这
个目的,C++采用两种方法:对象成员和继承。在
面向对象的程序设计中,大量使用继承和派生。类
的派生实际是一种演化、发展过程,即通过扩展、
更改和特殊化,从一个已知类出发建立一个新类。
通过类的派生可以建立具有共同关键特征的对象家
族,从而实现代码重用。这种继承和派生的机制对
于已有程序的发展和改进是极为有利的。
C++语言程序设计教程与实验 第十章 继承与派生
派生类同样也可以作为基类再派生新的类,这
样就形成了类的层次结构,类的继承和派生的层次
结构,可以说是人们对自然界中的事物进行分类、
分析和认识的过程在程序设计中的体现。现实世界
中的事物都是相互联系、相互作用的,人们在认识
过程中,根据事物的实际特征,抓住其共同特性和
细小差别,利用分类的方法进行分析和描述这些实
体或概念之间的相似点和不同点 。
C++语言程序设计教程与实验 第十章 继承与派生
C++语言程序设计教程与实验 第十章 继承与派生
派生类具有如下特点,
(1) 新的类可在基类所提供的基础上包含新的成员;
(2) 在新的类中可隐藏基类的成员函数;
(3) 可为新类重新定义成员函数。
基类与派生类的关系如下,
(1) 派生类是基类的具体化
(2) 派生类是基类定义的延续
(3) 派生类是基类的组合
10.1.2 派生类的定义
C++语言程序设计教程与实验 第十章 继承与派生
定义,在 C++中,派生类的定义如下,
class<派生类名 >:[继承方式 ]<基类名 >
{
派生类成员声明;
};
其中,
(1) class是类声明的关键字,用于告诉编译器下面声明的是一个类。
(2) 派生类名是新生成的类名。
在 C++程序设计中,进行派生类的定义,给出该类的成
员函数的实现之后,整个类就定义好了,这时就可以由它来
生成对象进行实际问题的处理。派生新类的过程,经历了三
个步骤:吸收基类成员,改造基类成员和添加新的成员,下
面分别介绍,
(1) 吸收基类成员
面向对象的继承和派生机制,其最主要的目的是实现代
码的重用和扩充。因此,吸收基类成员就是一个重用的过程
,而对基类成员进行调整、改造以及添加新成员就是原有代
码的扩充过程,二者是相辅相成的。
在 C++的类继承中,首先是基类成员的全盘吸收,这样,
派生类实际上就包含了它的所有基类的除构造函数和析构函
数之外的所有成员。尽管很多基类的成员,特别是非直接基
类的成员,在派生类中很可能根本不起作用
C++语言程序设计教程与实验 第十章 继承与派生
,也被继承下来,在生成对象时也要占据一定的内存
空间,造成资源浪费,我们可以对其进行改造。
(2) 改造基类成员
对基类成员的改造包括两方面,一是依靠派生类
的继承方式来控制基类成员的访问,二是对基类数据
成员或成员函数的覆盖,即在派生类中定义一个和基
类数据成员或成员函数同名的成员,由于作用域不同
,产生同名覆盖,基类中的成员就被替换成派生类中
的同名成员。
C++语言程序设计教程与实验 第十章 继承与派生
(3) 继承方式规定了如何访问从基类继承的成员。继承
方式关键字为 private,public和 protected,分别表示私有
继承、公有继承和保护继承,缺省情况下是私有 private继承。
类的继承方式指定了派生类成员以及类外对象对于从基类继
承来的成员的访问权限,这将在 10.1.3小节中详细介绍。
(4) 派生类成员指除了从基类继承来的所有成员之外,
还包括新增加的数据成员和成员函数。这些新增的成员正是
派生类不同于基类的关键所在,是派生类对基类的发展。当
重用和扩充已有的代码时,就是通过在派生类中新增成员来
添加新的属性和功能。
例如,定义如下的汽车类及其派生类小汽车类和卡车类,
C++语言程序设计教程与实验 第十章 继承与派生
例如, 定义如下的汽车类及其派生类小汽车类和卡车类,
class vehicle //定义基类 vehicle
{
public,//公有函数成员
void init vehicle(int in_wheels,float in_weight);//给
数据成员初始化
int get_wheels(); //取车轮数
float get_weight(); //取汽车重量
float wheelloading(); //车轮承重
private,//私有数据成员
int wheels; //车轮数
float weight; //表示汽车承重
};
C++语言程序设计教程与实验 第十章 继承与派生
在基类 vehicle的 基础上, 定义了如下的派生类 car和 truck,在
派生类中新增了一些数据成员和成员函数
class car:public vehicle //定义派生类 car
{
int passenger_load; //新增私有数据成员, 表示载客数
public,//新增公有成员函数
void intitialize(int in_wheels,float in_weight,int
people=4);
int passengers();
};
class truck:public vehicle
C++语言程序设计教程与实验 第十章 继承与派生
{
private,//新增私有数据成员
int passenger_load;
float weight_load;
puclic,//新增公有成员函数
void init_truck(int,float);
int passengers();
float weight_loads();
};
C++语言程序设计教程与实验 第十章 继承与派生
(3) 添加新的成员
继承与派生机制的核心是在派生类中加入
新 的成员,程序员可以根据实际情况的需要
,给派生类添加适当的数据成员和成员函数,
来实现必要的新功能。同时,在派生的过程中
,基类的构造函数和析构函数是不能被继承下
来的。在派生类中,一些特殊的初始化和扫尾
清理工作,也需要重新加入新的构造函数和析
构函数。
C++语言程序设计教程与实验 第十章 继承与派生
10.1.3 类的继承方式
C++语言程序设计教程与实验 第十章 继承与派生
继承的三种方式,
public(公有)继承,protected(保护)继承和 private(私有)继承
在面向对象程序中,基类的成员可以有 public(公有)、
protected(保护)和 private(私有)三种访问类型。在基类
内部,自身成员可以对任何一个其它成员进行访问,但是通过
基类的对象,就只能访问基类的公有成员。
派生类继承了基类的全部数据成员和除了构造、析构函数
之外的全部函数成员,但是这些成员的访问属性在派生的过程
中是可以调整的。从基类继承的成员,其访问属性由继承方式
控制。
对于不同的继承方式,会导致基类成员原来的访问属性在派生类
中有所变化。 表 10.1列出了不同继承方式下基类成员访问属性的
变化情况。
说明,
该表第 1列给出 3种继承方式,第 1行给出基类成员的 3种访
问属性。其余单元格内容为基类成员在派生类中的访问属性。从
表中可以看出,
(1) 基类的私有成员在派生类中均是不可访问的, 它只能由基类
的成员访问 。
(2) 在公有继承方式下, 基类中的公有成员和保护成员在派生类
中的访问属性不变 。
(3) 在保护继承方式下, 基类中的公有成员和保护成员在派生类
中均为保护的
(4) 在私有继承方式下, 基类中的公有成员和保护成员在派生类
中均为私有的
C++语言程序设计教程与实验 第十章 继承与派生
从基类 vehicle( 汽车 ) 公有派生 car( 小汽车 ) 类, car类
继承了 vehicle类的全部特征, 同时, car类自身也有一些特点,
这就需要在继承 vehicle类的同时添加新的成员 。
#include<iostream.h>
class vehicle //基类 vehicle类的声明
{
private,//私有数据成员
float weight;
int wheels;
public,//公有函数成员
C++语言程序设计教程与实验 第十章 继承与派生
vehicle(int in_wheels,float in_weight)
{wheels=in_wheels;weight=in_weight;}
int get_wheels(){return wheels;}
float get_weight(){return weight;}
};
class car:public vehicle //派生类 car
类的声明
{
private,//新增私有数据成员
int passenger_load;
public,//新增公有函数成

C++语言程序设计教程与实验 第十章 继承与派生
{
private,//新增私有数据成员
int passenger_load;
float weight_load;
puclic,//新增公有成员函数
void init_truck(int,float);
int passengers();
float weight_loads();
};
C++语言程序设计教程与实验 第十章 继承与派生
car(int in_wheel,float in_weight,int
people=5):vehicle(in_wheel,in_weight)
{passenger_load=people;}
int get_passengers(){return passenger_load;}
};
void main()
{
car bm(4,1000); //声明 car类的对象
cout<<"The message of
bm(wheels,weight,passengers):"<<endl;
cout<<bm.get_wheels()<<"," ; //访问派生类从基类继
承来的公有函数
C++语言程序设计教程与实验 第十章 继承与派生
C++语言程序设计教程与实验 第十章 继承与派生
cout<<bm.get_weight()<<","; //访问派生类从基类继承来
的公有函数
cout<<bm.get_passengers()<<endl;// 访问派生类的公有函

}
程序的运行结果,
The message of bm(wheels,weight,passengers),
4,1000,5
表 10.1 不同继承方式下基类成员的访问属性
C++语言程序设计教程与实验 第十章 继承与派生
访问属 性
继 承方式
public
protected
private
public
public
protected
不可 访问 的
protected
protected
protected
不可 访问 的
private
private
private
不可 访问 的
1,公有继承
派生类的其它成员可以直接访问基类的公有成员
和保护成员 。 其它外部使用者只能通过派生类的对象
访问继承来的公有成员 。 而无论是派生类的成员还是
派生类的对象, 都无法访问基类的私有成员
【 例 10.1】 公有继承
C++语言程序设计教程与实验 第十章 继承与派生
保护成员与私有成员唯一的不同是当发
生派生后,处在基类 protected 区的成员可
被派生类直接访问,而私有成员在派生类中
是不可访问的。在同一类中私有成员和保护
成员的用法完全一样。
需要注意的是
从基类 vehicle( 汽车 ) 公有派生 car( 小汽车 ) 类, car类继承了
vehicle类的全部特征, 同时, car类自身也有一些特点, 这就需要在
继承 vehicle类的同时添加新的成员 。
#include<iostream.h>
class vehicle //基类 vehicle类的声明
{
private,//私有数据成员
float weight;
int wheels;
public,//公有函数成员
vehicle(int in_wheels,float in_weight)
{wheels=in_wheels;weight=in_weight;}
int get_wheels(){return wheels;}
C++语言程序设计教程与实验 第十章 继承与派生
float get_weight(){return weight;}
};
class car:public vehicle //派生类 car类的
声明
{
private,//新增私有数据成员
int passenger_load;
public,//新增公有函数成员
car(int in_wheel,float in_weight,
int people=5):vehicle(in_wheel,in_weight)
{passenger_load=people;}
int get_passengers(){return passenger_load;}
};
C++语言程序设计教程与实验 第十章 继承与派生
void main()
{ car bm(4,1000); //声明 car类的对象
cout<<"The message of
bm(wheels,weight,passengers):"<<endl;
cout<<bm.get_wheels()<<“,”; //访问派生类从基类继承来的
公有函数
cout<<bm.get_weight()<<“,”; //访问派生类从基类继承来的公
有函数
cout<<bm.get_passengers()<<endl;// 访问派生类的公有函数
}
程序的运行结果,
The message of bm(wheels,weight,passengers),
4,1000,5
C++语言程序设计教程与实验 第十章 继承与派生
这里首先声明了基类 vehicle。派生类 car继承了 vehicle
类的全部成员(构造和析构函数除外)。因此,在派生类中,
实际所拥有的成员就是从基类继承过来的成员与派生类新声
明的成员的总和。继承方式为公有继承。这时基类中的公有
成员在派生类中的访问属性保持原样,派生类的成员函数及
对象可以访问基类的公有成员,但是无法访问基类的私有数
据(例如基类的 wheels和 weight)。
基类原有的外部接口(如基类的 get_wheels()和
get_weight()函数)变成了派生类外部接口的一部分。当然,
派生类自己新增的成员之间都是可以互相访问的。
car类继承了 vehicle类的成员, 也就实现了代码的重用
。 同时, 通过新增成员, 加入了自身的独有特征, 实现了程
序的扩充 。
C++语言程序设计教程与实验 第十章 继承与派生
2.私有继承
当类的继承方式为私有时, 基类的 public成员和
protected成员被继承后作为派生类的私有成员, 派生类的其
它成员可以直接访问它们, 但是在类外部通过派生类的对象
无法访问 。
【 例 10.2】 私有继承
在私有继承情况下,为了保证基类的部分外部接口特征
能够在派生类中也存在,就必须在派生类中重新定义同名的
成员函数,同例 10.1相比较,本例对程序修改的只是派生类的
内容,基类和主函数部分没有做任何改动。我们可以由此看
到面向对象程序设计封装性的优越性,car类的外部接口不变,
内部成员的实现做了改造,根本没有影响到程序的其它部分,
这正是面向对象程序设计可重用与性与可扩充性的一个实际
体现。
C++语言程序设计教程与实验 第十章 继承与派生
【 例 10.2】 私有继承
#include<iostream.h>
class vehicle //基类 vehicle类的声明
{
private,//私有数据成员
int wheels;
float weight;
public,//公有函数成员
vehicle(int in_wheels,float in_weight)
{wheels=in_wheels;weight=in_weight;}
int get_wheels()
{return wheels;}
C++语言程序设计教程与实验 第十章 继承与派生
float get_weight()
{return weight;}
};
class car:private vehicle //定义派生类 car类
{
private,//新增私有数据成员
int passenger_load;
public,//新增公有函数成员
car(int in_wheels,float in_weight,int people=5),
vehicle(in_wheels,in_weigh)
C++语言程序设计教程与实验 第十章 继承与派生
{passenger_load=people;}
int get_wheels()
{return vehicle::get_wheels();} //重新定义 get_wheels()
float get_weight()
{return vehicle::get_weight();}//重新定义 get_weight()
int get_passengers()
{return passenger_load;}
};
void main()
C++语言程序设计教程与实验 第十章 继承与派生
{
car bm(4,1000); //定义 car类对象
cout<<"The message of
bm(wheels,weight,passengers):"<<endl;
cout<<bm.get_wheels()<<"," //输出小汽车的信息
<<bm.get_weight()<<","
<<bm.get_passengers()<<endl;
}
程序的运行结果,
The message of bm(wheels,weight,passengers),
4,1000,5
C++语言程序设计教程与实验 第十章 继承与派生
3.保护继承
保护继承中, 基类的 public( 公有 ) 和 protected( 保护 )
成员都以保护成员的身份出现在派生类中, 而基类的 private
( 私有 ) 成员不可访问 。
【 例 10.3】 保护继承。
在保护继承情况下,为了保证基类的部分外部接口特征
能够在派生类中也存在,就必须在派生类中重新定义同名的
成员函数。根据同名覆盖的原则,在主函数中自然调用的是
派生类的函数 。
C++语言程序设计教程与实验 第十章 继承与派生
【 例 10.3】
保护继承 。
#include<iostream.h>
class vehicle //定义基类 vehicle
{
private,//私有数据成员
int wheels;
protected,//保护数据成员
float weight;
public,//公有函数成员
vehicle(int in_wheels,float in_weight)
{wheels=in_wheels;weight=in_weight;}
C++语言程序设计教程与实验 第十章 继承与派生
int get_wheels()
{return wheels;}
float get_weight()
{return weight;}
};
class car:protected vehicle //定义派生类 car
{
private,//新增私有数据成员
int passenger_load;
public,//新增公有函数成员
car(int in_wheels,float in_weight,int people=5),
vehicle(in_wheels,in_weight)
{passenger_load=people;}
C++语言程序设计教程与实验 第十章 继承与派生
int get_wheels() //重新定义 get_wheels()
{return vehicle::get_wheels();}
float get_weight() //重新定义 get_weight()
{return weight;}
int get_passengers()
{return passenger_load;}
};
void main()
{
car bm(4,1000); //声明 car类的对象
cout<<"The message of
bm(wheels,weight,passengers):"<<endl;
C++语言程序设计教程与实验 第十章 继承与派生
cout<<bm.get_wheels()<<"," //输出小汽车的信息
<<bm.get_weight()<<","
<<bm.get_passengers()<<endl;
}
程序的运行结果,
The message of bm(wheels,weight,passengers),
4,1000,5
C++语言程序设计教程与实验 第十章 继承与派生
1.派生类的构造函数
在下面两种情况下, 必须定义派生类的构造函数:派生
类本身要构造函数;在定义派生类对象时, 其相应的基类对
象需调用带有参数的构造函数 。
派生类对象的初始化也是通过派生类的构造函数实现的。
具体来说,就是对该类的数据成员赋初值。派生类的数据成
员由所有基类的数据成员与派生类新增的数据成员共同组成,
如果派生类新增成员中包括有内嵌的其它类对象,派生类的
数据成员中实际上还间接包括了这些对象的数据成员。
10.1.4 派生类的构造函数和析构函数
C++语言程序设计教程与实验 第十章 继承与派生
因此,初始化派生类对象时,就要对基类数据
成员、新增数据成员和成员对象的数据成员进行初
始化。派生类的构造函数需要以合适的初值作为参
数,隐含调用基类和新增的内嵌对象成员的构造函
数来初始化它们各自的数据成员,然后再加入新的
语句对新增普通数据成员进行初始化。
C++语言程序设计教程与实验 第十章 继承与派生
派生类构造函数声明的一般语法形式如下,
派生类构造函数 (参数表 ):基类构造函数 (参数表 ),对象
成员 1的构造函数 (参数表 )……, 对象成员 n的构造函数 (参数
表 )
{ 派生类新增成员的初始化语句;
其中,
? (1) 派生类的构造函数名与派生类名相同 。
? (2) 参数表需要列出初始化基类数据, 新增内嵌对象数
据及新增一般成员数据所需要的全部参数 。
? (3) 冒号之后,列出需要使用参数进行初始化的基类名
和内嵌成员名及各自的参数表,各项之间用逗号分隔。
在定义派生类对象时构造函数的执行顺序是先祖先 ( 基
类 ), 再客人 ( 对象成员 ), 后自己 ( 派生类本身 ) 。
【 例 10.4】 构造函数的调用顺序。
C++语言程序设计教程与实验 第十章 继承与派生
【 例 10.4】 构造函数的调用顺序 。
#include<iostream.h>
class data
{
int x;
public,
data(int x)
{
data::x=x;
cout<<"class data\n";
}
};
class a
C++语言程序设计教程与实验 第十章 继承与派生
{
data d1;
public,
a(int x):d1(x)
{cout<<"class a\n";}
};
class b:public a
{
data d2;
public,
b(int x):a(x),d2(x)
{cout<<"class b\n";}
};
C++语言程序设计教程与实验 第十章 继承与派生
class c:public b
{
public,
c(int x):b(x)
{cout<<"class c\n";}
};
void main()
{
c object(5);
}
程序的运行结果
class data class a class data class b class c
C++语言程序设计教程与实验 第十章 继承与派生
2,派生类的析构函数
派生类和基类的析构函数是独立的, 因为析构函数是不
带参数的 。 派生类析构函数的功能与没有继承关系的类中析
构函数的功能一样, 也是在对象消亡之前进行一些必要的清
理工作 。 在派生过程中, 基类的析构函数不能继承, 如果需
要析构函数的话, 就要在派生类中重新定义 。 析构函数没有
类型, 也没有参数, 如果没有显式定义过某个类的析构函数
,系统会自动生成一个默认的析构函数, 完成清理工作 。
派生类析构函数的定义方法与没有继承关系的类中析构
函数的定义方法完全相同,只要在函数体中负责把派生类新
增的非对象成员的清理工作做好就够了,系统会自己调用基
类及成员对象的析构函数来对基类及对象成员进行清理。析
构函数的执行顺序和构造函数正好严格相反 —— 先自己 (派生
类本身 ),再客人 (对象成员 ),后祖先 (基类 )。
【 例 10.5】 析构函数的调用顺序 。
C++语言程序设计教程与实验 第十章 继承与派生
【 例 10.5】 析构函数的调用顺序。
#include<iostream.h>
class person
{
char *name;
int age;
char*add;
public,
person()
{cout<<"the constructor of class person!\n";}
~person()
{cout<<"the destructor of class person!\n";}
};
C++语言程序设计教程与实验 第十章 继承与派生
class student:public person
{
char *department;
int level;
public,
student()
{cout<<"the constructor of class student!\n";}
~student()
{cout<<"the destructor of class student!\n";}
}; class teacher:public person
{
C++语言程序设计教程与实验 第十章 继承与派生
char *major;
float salary;
public,
teacher()
{cout<<"the constructor of class teacher!\n";}
~teacher()
{cout<<"the destructor of class teacher!\n";}
};
void main()
{
student d1;
teacher d2;
}
C++语言程序设计教程与实验 第十章 继承与派生
程序的运行结果是,
the constructor of class person!
the constructor of class student!
the constructor of class person!
the constructor of class teacher!
the destructor of class teacher!
the destructor of class person!
the destructor of class student!
the destructor of class person!
C++语言程序设计教程与实验 第十章 继承与派生
10.2 多重继承
在派生类的声明中, 基类名可以有一个, 也可以有多个 。
如果只有一个基类名, 则这种继承方式称为单继承;如果
基类名有多个, 则这种继承方式称为多继承, 这时的派生
类同时得到了多个已有类的特征 。 在多继承中, 各个基类
名之间用逗号隔开 。 多继承的语法如下,
从这个一般形式可以看出,每个基类有一个继承方式来
限制其中成员在派生类中的访问权限,其规则和单一继承
情况是一样的,多重继承可以看作是单一继承的扩展,单
继承可以看作是多继承的一个最简单的特例。
C++语言程序设计教程与实验 第十章 继承与派生
10.2.1 多继承的概念和定义
class<派生类名 >:[继承方式 ]基类名 1,[继承方式 ]基类名
2,...,[继承方式 ]基类名 n
{
定义派生类自己的成员;
};
在派生过程中,派生出来的新类也同样可以作为基类再
继续派生新的类。此外,一个基类可以同时派生出多个派生
类。也就是说,一个类从父类继承来的特征也可以被其它新
的类所继承,一个父类的特征,可以同时被多个子类继承。
这样就形成了一个相互关联的类的家族,称为类族。在类族
中,直接参与派生出某类的基类称为直接基类;基类的基类
甚至更高层的基类称为间接基类。
C++语言程序设计教程与实验 第十章 继承与派生
1,二义性的两种情况
(1) 当一个派生类是多重派生也就是由多个基类派生而
来时, 假如这些基类中的成员有成员名相同的情况, 如果使
用一个表达式引用了这些同名的成员, 就会造成无法确定是
引用哪个基类的成员, 这种对基类成员的访问就是二义性的 。
要避免此种情况, 我们可以使用成员名限定来消除二义
性, 也就是在成员名前用对象名及基类名来限定, 例如,
【 例 10.6】 多继承中的二义性问题。
C++语言程序设计教程与实验 第十章 继承与派生
10.2.1 多继承的概念和定义
【 例 10.6】 多继承中的二义性问题 。
#include<iostream.h>
class Bed
{
public,
Bed(),weight(){}
void Sleep(){ cout <<"Sleeping...\n"; }
void SetWeight(int i){ weight =i; }
protected,
int weight;
};
class Sofa
C++语言程序设计教程与实验 第十章 继承与派生
{
public,
Sofa(),weight(){}
void WatchTV(){ cout <<"Watching TV.\n"; }
void SetWeight(int i){ weight =i; }
protected,
int weight;
};
class SleeperSofa,public Bed,public Sofa //多重继承
{
public,
C++语言程序设计教程与实验 第十章 继承与派生
SleeperSofa(){}
void FoldOut(){ cout <<"Fold out the sofa.\n"; }
};
void main()
{
SleeperSofa ss;
ss.WatchTV();
ss.FoldOut();
ss.Sleep();
ss.SetWeight(20);//出现二义性
}
C++语言程序设计教程与实验 第十章 继承与派生
(2) 如果一个派生类从多个基类中派生, 而这些基类又
有一个共同的基类, 则在这个派生类中访问这个共同基类中
的成员时会产生两义性 。 要避免此种情况, 可以利用 10.3节
讲到的虚基类 。
2,C++作用域规则:当基类中的成员名字在派生类中再
次声明, 则派生类中的名字就屏蔽掉基类中相同的名字 (也就
是派生类的自定义成员与基类成员同名时, 派生类的成员优
先 )。 如果要使用被屏蔽的成员可由作用域操作符实现 。 它的
形式是类名,:类标识符 。 作用域操作符不仅可以用在类中,
而且可以用在函数调用时 。
C++语言程序设计教程与实验 第十章 继承与派生
(3), 支配规则:一个派生类中的名字将优先于
它的基类中相同的名字 。 这时二者之间不存在二义性,
当选择该名字时, 使用支配者 (派生类中 )的名字 。
C++语言程序设计教程与实验 第十章 继承与派生
所谓赋值兼容规则就是,在公有派生的情况下,一个派
生类的对象可以作为基类的对象来使用的地方 (在公有派生的
情况下,每一个派生类的对象都是基类的一个对象,它继承
了基类的所有成员并没有改变其访问权限 )。
具体的说,有三种情况可以把一个公有派生类的对象
作为基类对象来使用的,
1,派生的对象可以赋给基类的对象 。 如,(约定
derived是从类 base公有派生而来的 )
C++语言程序设计教程与实验 第十章 继承与派生
10.2.3 赋值兼容规则
derived d;
base b;
b=d;
2,派生类的对象可以初始化基类的引用 。 如,
derived;
base &br=d;
3,派生类的对象的地址可以赋给指向基类的指针 。
如,
derived d;
base *pb=&d;
C++语言程序设计教程与实验 第十章 继承与派生
当在多条继承路径上有一个公共的基类, 在这些路径中
的某几条路径汇合处, 这个公共的基类就会产生多个实例
( 或多个副本 ), 若想只保存这个基类的一个实例, 可以将
这个公共基类说明为虚基类 。 从基类派生新类时, 使用关键
字 virtual可以将基类说明成虚基类 。 一个基类, 在定义他的
派生类时, 在作为某些派生类的虚基类的同时, 又作为另一
些派生类的非虚基类, 这种情况是允许存在的 。
【 例 10.7】 利用虚基类避免产生二义性 。
C++语言程序设计教程与实验 第十章 继承与派生
10.3 虚基类
10.3.1 虚基类的概念
【 例 10.7】 利用虚基类避免产生二义性 。
#include<iostream.h>
class Furniture
{
public,
Furniture(){}
void SetWeight(int i){ weight =i; }
int GetWeight(){ return weight; }
protected,
int weight;
};
class Bed,virtual public Furniture // Furniture类作为
Bed类的虚基类
C++语言程序设计教程与实验 第十章 继承与派生
{
public,
Bed(){}
void Sleep(){ cout <<"Sleeping...\n"; }
};
class Sofa,virtual public Furniture
{
public,
Sofa(){}
void WatchTV(){ cout <<"Watching TV.\n"; }
};
C++语言程序设计教程与实验 第十章 继承与派生
class SleeperSofa,public Bed,public Sofa
{
public,
SleeperSofa(),Sofa(),Bed(){}
void FoldOut(){ cout <<"Fold out the sofa.\n"; }
};
void main()
{
SleeperSofa ss;
ss.SetWeight(20);
cout <<ss.GetWeight() <<endl;
}
C++语言程序设计教程与实验 第十章 继承与派生
在多个基类之间严格按照派生类定义时从左到右的顺序
来排列先后 。 而析构函数的调用顺序刚好与构造函数的相反 。
如果基类中有虚基类, 则构造函数的调用顺序采用下列规则,
(1) 虚基类的构造函数在非虚基类之前调用 。
(2) 若同一层次中包含多个虚基类, 这些虚基类的构造
函数按照他们说明的次序调用;
(3) 若虚基类由非虚基类派生而来, 则仍然先调用基类
构造函数, 再调用派生类的构造函数 。
C++语言程序设计教程与实验 第十章 继承与派生
10.3.2多继承的构造函数和析构函数
特别需要注意, 当一个派生类同时有多个基类时, 对于
所有需要给予参数进行初始化的基类, 都要显式给出基类名
和参数表 。 对于使用默认构造函数的基类, 可以不给出类名
。 同样, 对于对象成员, 如果是使用默认构造函数, 也不需
要写出对象名和参数表 。 而对于单继承, 就只需要写一个基
类名就可以了 。
【 例 10.8】 虚基类的使用 。
C++语言程序设计教程与实验 第十章 继承与派生
#include<iostream.h>
class base
{
public,
base()
{cout<<"this is base class!\n";}
};
class base2
{
public,
base2()
{cout<<"this is base2 class!\n";}
};
C++语言程序设计教程与实验 第十章 继承与派生
class level1:public base2,virtual public base
{
public,
level1()
{cout<<"this is level1 class!\n";}
};
class level2:public base2,virtual public base
{
public,
level2()
{cout<<"this is level2 class!\n";}
};
C++语言程序设计教程与实验 第十章 继承与派生
class toplevel:public level1,virtual public level2
{
public,
toplevel()
{cout<<"this is toplevel class!\n";}
};
void main()
{
toplevel topobj;
}
C++语言程序设计教程与实验 第十章 继承与派生
程序的运行结果,
this is base class!
this is base2 class!
this is level2 class!
this is base2 class!
this is level1 class!
this is toplevel class!
C++语言程序设计教程与实验 第十章 继承与派生
【 例 10.9】 多继承中构造函数和析构函数的调用顺序
#include<iostream.h>
class OBJ1
{ public,
OBJ1(){cout<<"OBJ1\n";}
~OBJ1(){cout<<"destructing OBJ1"<<endl;}
};
class OBJ2
{ public,
OBJ2(){ cout<<"OBJ2\n"; }
~OBJ2(){cout<<"destructing OBJ2"<<endl;}
};
class Base1
C++语言程序设计教程与实验 第十章 继承与派生
{
public,
Base1(){ cout<<"Base1\n"; }
~Base1(){cout<<"destructing Base1"<<endl;}
};
class Base2
{
public,
Base2(){ cout<<"Base2\n"; }
~Base2(){cout<<"destructing Base2"<<endl;}
};
class Base3
{
C++语言程序设计教程与实验 第十章 继承与派生
public,
Base3(){ cout<<"Base3\n"; }
~Base3(){cout<<"destructing Base3"<<endl;}
};
class Base4{
public,
Base4(){ cout<<"Base4\n"; }
~Base4(){cout<<"destructing Base4"<<endl;}
};
class Derived:public Base1,virtual public Base2,public
Base3,virtual public Base4
{
public,
C++语言程序设计教程与实验 第十章 继承与派生
Derived():Base4(),Base3(),Base2(),Base1(),obj2(),obj1()
{
cout <<"Derived ok.\n";
}
~Derived(){cout<<"destructing Derived"<<endl;}
protected,
OBJ1 obj1;
OBJ2 obj2;
};
void main()
{
Derived aa;
}
C++语言程序设计教程与实验 第十章 继承与派生
程序的运行结果,
Base2 Base4 Base1 Base3
OBJ1
OBJ2
Derived ok,
destructing Derived
destructing OBJ2
destructing OBJ1
destructing Base3
destructing Base1
destructing Base4
destructing Base2
C++语言程序设计教程与实验 第十章 继承与派生