C++大学基础教程
第9章 继承与派生
北京邮电大学电信工程学院
计算机技术中心
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-2-
第9章 继承与派生
9.1 继承的概念
9.2 继承方式
9.3 派生类构造函数的定义
9.4 多继承
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-3-
第9章 继承与派生
软件重用
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-4-
第9章 继承与派生
类具有封装性、继承性和多态性
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-5-
继承的概念
自行车
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-6-
继承的概念
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-7-
继承的概念
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-8-
继承的概念
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-9-
继承的概念
Bicycle
Mountain
Bikes
Racing
Bikes
Tandem
Bikes
is-a relationships
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-10-
第9章 继承与派生
主要介绍:
? 继承和派生的概念
? 继承方式;
? 派生类的构造函数与析构函数;
? 多继承中的二义性
? 虚基类。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-11-
9.1 继承的概念
类的继承 是在现有类的基础之上,创建新类的
机制 。 称现有的类为 基类 , 新建立的类为 派生
类 。
? 新类继承了基类的属性和行为
? 新类是基类的特殊情况。
不必从 “草稿 ”开始创建特殊的程序对象
继承是处理 “特殊情况 ”的面向对象编程机制
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-12-
派生类的定义格式
class 派生类名:继承方式 基类名
{
public:
//派生类公有成员 …
private:
//派生类私有成员 …
}
派生类只有
一个直接基
类为 单继承
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-13-
例 : 定义基类 shape
class shape
{
private:
int m_x,m_y; //位置
char m_color; //颜色
public:
void setposition(int x, int y);
void setcolor(char color);
int getx();
int gety();
char getcolor();
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-14-
定义派生类 (等边三角形类 )
class Triangle: public Shape
{
public:
Triangle(int x, int y, char color='R', float slen = 1);
float GetSideLength() const;
void SetTriangle(int x, int y, char color, float slen);
void Draw();
private:
float m_SideLength;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-15-
派生新类:
circle 圆形
rectangle 矩形
triangle 三角形
基类称为 父类
派生类称为 子类
shape
circle rectangle
triangle
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-16-
派生类的定义格式
class 派生类名:继承方式 基类名1, … 继承方式 基类名n
{
public:
//派生类公有成员 …
private:
//派生类私有成员 …
}
有多个基类
派生类有多
个基类为
多继承
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-17-
例:已有基类base1,base2,base3,定义派生类deriver
class deriver:public base1,public base2, private base3
{
private:
int m_derdata;
public:
void derfunction();
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-18-
注意
每一个 “继承方式 ”,只用于限制对紧随其后之
基类的继承。
类 的继承方式是派生类对基类成员的继承方
式。
类的继承方式指定了类外对象对于派生类从基
类继承来的成员的访问权限。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-19-
直接基类和间接基类
class base
{……
};
class deriver1:public base
{ ……
};
class deriver2:public deriver1
{……
}
? 父类被称为子
类的 直接基类
? 父类的父类或
更高层次的父
类被称为这个
子类的 间接基
类
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-20-
派生与继承的实例
公司人员管理系统 :
小型公司人员分为: 经理、兼职技术人员、销售经理和
兼职推销员.
要求:
存储所有人员的姓名、编号、级别、当月薪水,计算月薪
总额并显示全部信息。
人员编号在生成人员信息时同时生成,每输入一个人员信
息编号顺序加1。
程序能够对不同人员按不同方法提升级别,月薪的计算
方法是:
? 经理拿固定月薪;
? 兼职技术人员按工作小时数领取月薪;
? 兼职推销员的报酬按该推销员当月销售额提成;
? 销售经理既拿固定月薪也领取销售提成。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-21-
派生与继承的实例
分析:
·描述全体职员的共性(基类)
·描述每一类特殊人员(派生类)
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-22-
class employee
{
protected:
char *name; //姓名
int individualEmpNo; //个人编号
int grade; //级别
float accumPay; //月薪总额
static int employeeNo; //本公司职员编号目前最大值
public:
employee(); //构造函数
~employee(); //析构函数
void pay(); //计算月薪函数
void promote(int);//升级函数
void displayStatus(); //显示人员信息
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-23-
class technician: public employee //兼职技术人员类
{
private:
float hourlyRate; //每小时酬金
int workHours; //当月工作时数
public:
technician(); //构造函数
void pay(); //计算月薪函数
void displayStatus(); //显示人员信息
};
新增加的成员
同名覆盖,改造基类成员
派生类的成员:
1. 从基类继承的成员;
2. 改造基类成员;
3. 添加派生类新成员.
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-24-
9.2 继承方式
三种继承方式
public、protected、private
不同继承方式的影响主要体现在:
? 派生类 成员 对基类成员的访问控制。
? 派生类 对象 对基类成员的访问控制。
定义派生类时要声明继承方式
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-25-
9.2.1. 派生类的定义
派生类的定义形式:
class 派生类名:继承方式 基类1,
继承方式 基类2, … ,继承方式 基类n
{
派生类成员声明;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-26-
例如:
设已有基类base1和base2,定义派生类
deriver.
class deriver: public base1,private base2
{ private:
int newmember;
public:
void newfun();
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-27-
单继承情况,派生类的定义
class 派生类名:继承方式 基类名
{
派生类成员声明
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-28-
例9.1 图形类及其派生类的声明
class Shape
{
public:
Shape(int x=0, int y=0, char c = 'R');
int GetX() const;
void SetX( int x);
int GetY() const;
void SetY( int x);
char GetColor() const;
void SetColor(char c);
protected:
char m_color;
int m_x;
int m_y;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-29-
class Circle : public Shape
{
public:
Circle(int x, int y, float r=1, char color='R');
float GetRadius () const;
void SetCircle(int x, int y, float r, char color);
void Draw();
private:
float m_Radius;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-30-
class Triangle: public Shape
{
public:
Triangle(int x, int y, char color='R', float slen = 1);
float GetSideLength() const;
void SetTriangle(int x, int y, char color, float slen);
void Draw();
private:
float m_SideLength;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-31-
class Rectangle: public Shape
{
public:
Rectangle(int x, int y, char color, int length=10,
int width=10);
int GetWidth() const;
int GetHeight() const;
void Draw();
void SetRectangle (int x, int y, char color, int
length, int width);
private:
int m_Width;
int m_Length;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-32-
? 派生类的成员包括:(1)继承基类的成员,(2)派生
类定义时声明的成员。
? 派生类自己增加的成员,完成两个需求:(1)修改基类
成员,(2)描述新的特征或方法。
从基类
继承的
成员
m_color;
m_x; m_y;
GetX();SetX();
GetY();SetY();
GetColor();
SetColor();
m_Radius;
GetRadius ()
SetCircle();
Draw();
m_color;
m_x; m_y;
GetX();SetX();
GetY();SetY();
GetColor();
SetColor();
m_SideLength;
GetSideLength();
SetTriangle();
Draw();
m_color;
m_x; m_y;
GetX();SetX();
GetY();SetY();
GetColor();
SetColor();
m_Width; m_Length;
GetWidth();
GetHeight();
Draw();
SetRectangle();
派生类
增加的
成员
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-33-
同名覆盖
派生类修改基类的成员,是在派生类中声明了
一个与基类成员同名的新成员。在派生类作用
域内或者在类外通过派生类的对象直接使用这
个成员名,只能访问到派生类中声明的同名新
成员,这个新成员覆盖了从基类继承的同名成
员,这种情况称为 同名覆盖 。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-34-
class Shape
{
public:
┇
void Draw(){};
protected:
┇
};
class Triangle: public Shape
{
public:
Triangle(int x, int y, char
color='R', float slen = 1);
float GetSideLength() const;
void SetTriangle(int x, int
y, char color, float slen);
void Draw();
private:
float m_SideLength;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-35-
例9.2 同名覆盖示例
#include<iostream>
using namespace std;
class base
{
public:
void function(){cout<<"function of class base"<<endl;}
};
class deriver: public base
{
public:
void function(){cout<<"function of class
deriver"<<endl;}
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-36-
void main()
{
deriver derobj;
derobj.function();
}
输出结果:
function of class deriver
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-37-
例9.3 派生类成员函数对基类同名函
数的改进。
//number.h
class Number
{
protected:
int m_number;
public:
int GetNumber(){ return m_number; }
void SetNumber(int n){ m_number=n;}
void Prime();
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-38-
//number.cpp
#include<iostream>
#include "number.h"
using namespace std;
void Number::Prime()
{ int i;
for(i=2; i<m_number; i++) //找 m_number的因数
{ if(m_number %i==0) break;}
if(m_number ==i) //判断 m_number是否被小于 m_number的数整除
cout <<m_number <<" is prime"<<endl;
else
cout <<m_number <<" isn't prime"<<endl;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-39-
//DerNumber.h
class DerNumber: public Number
{
public:
void Prime();
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-40-
//DerNumber.cpp
#include<iostream>
#include<cmath>
#include "number.h"
#include "DerNumber.h"
using namespace std;
void DerNumber::Prime()
{ double sqrtm=sqrt(m_number); //用到 math.h
int i;
for(i=2; i<=sqrtm; i++)
{ if(m_number %i==0) break;}
if(sqrtm<i) cout <<m_number <<" is prime.\n";
else cout <<m_number <<" isn't prime.\n";
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-41-
//使用模块
#include<iostream>
#include<ctime>
#include "number.h"
#include "DerNumber.h"
using namespace std;
void main()
{
Number aNum;
DerNumber aDerNum;
clock_t start, finish;
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-42-
double elapsed_time1,elapsed_time2;
int i;
unsigned int max(100000);
time( &start );
for(i=10000;i<=max;i++)
{
aNum.SetNumber(i);
cout<<aNum.GetNumber()<<" "<<endl;
aNum.Prime();
}
time( &finish );
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-43-
elapsed_time1 = difftime( finish, start );
time( &start );
for(i=10000;i<=max;i++)
{
aDerNum.SetNumber(i);
cout<<aDerNum.GetNumber()<<" "<<endl;
aDerNum.Prime();
}
time( &finish );
elapsed_time2 = difftime( finish, start );
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-44-
cout<<"Delay for using Number class: "<<elapsed_time1
<<" seconds"<<endl;
cout<<"Delay for using DerNumber class: "
<<elapsed_time2<<" seconds"<<endl;
}
输出结果(部分):
Delay for using Number class: 157 seconds
Delay for using DerNumber class: 151 seconds
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-45-
9.2.2. 继承的访问控制
派生类继承了基类中除构造函数和析
构函数之外的所有成员。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-46-
9.2.2. 继承的访问控制
三种继承方式 :
? 公有继承 (public)
? 私有继承 (private)
? 保护继承 (protected)
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-47-
9.2.2. 继承的访问控制
不同的继承方式使得派生类从基类继承的成员 具 有
不同的访问控制权限,以实现数据的安全性和共享
性控制。
不同继承方式决定的不同访问控制权限体现在:
? 派生类的成员函数 对其继承的基类成员的访问控制;
? 其它模块通过派生类对象 对其继承的基类成员的访问控
制。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-48-
1. 公有继承
公有继承的派生类定义形式:
class 派生类名: public 基类名
{
派生类新成员定义;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-49-
公有继承
public是定义公有继承方式的关键字
公有继承方式定义的派生类,继承了基类中 除构造
函数和析构函数外 的其余成员:公有成员、保护成
员和私有成员
被继承的基类成员在派生类中仍将保持其原来的访
问属性。
派生类的成员函数 可以访问基类的公有成员和保护
成员,不能访问基类的私有成员;
派生类以外的其它函数 可以通过派生类的对象,访
问从基类继承的公有成员, 但不能访问从基类继承
的保护成员和私有成员。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-50-
class Point //基类 Point类的定义
{
public: //公有函数成员
void InitP(float xx=0, float yy=0) {X=xx;Y=yy;}
void Move(float xOff, float yOff) {X+=xOff;Y+=yOff;}
float GetX() {return X;}
float GetY() {return Y;}
private: //私有数据成员
float X,Y;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-51-
class Rectangle: public Point //派生类声明部分
{
public: //新增公有函数成员
void InitR(float x, float y, float w, float h)
{ InitP(x,y); //访问基类公有成员函数
W=w;H=h;}
float GetH() {return H;}
float GetW() {return W;}
private: //新增私有数据成员
float W,H;
};
z派生类中的 成员函
数 可以直接访问基类
中的public和
protected成员 ,但不
能访问基类的private
成员。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-52-
main()
{
Rectangle rect;
cout<<rect.X; //?
cout<<rect.GetX();//??
}
z使用派生类的 对象
只能访问基类的
public成员
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-53-
class Rectangle: public Point //派生类声明部分
{
public: //新增公有函数成员
void InitR(float x, float y, float w, float h)
{X=x; Y=y; //?访问基类私有成员
W=w;H=h;}
float GetH() {return H;}
float GetW() {return W;}
private: //新增私有数据成员
float W,H;
};
z派生类中的 成员函
数 可以直接访问基类
中的public和
protected成员, 但不
能访问基类的private
成员。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-54-
class Point //基类 Point类的定义
{
public:
void InitP(float xx=0, float yy=0) {X=xx;Y=yy;}
void Move(float xOff, float yOff) {X+=xOff;Y+=yOff;}
float GetX() {return X;}
float GetY() {return Y;}
protected:
float X,Y;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-55-
class Rectangle: public Point //派生类声明部分
{
public: //新增公有函数成员
void InitR(float x, float y, float w, float h)
{X=x; Y=y; //??访问基类的保护成员
W=w;H=h;}
float GetH() {return H;}
float GetW() {return W;}
private: //新增私有数据成员
float W,H;
};
z派生类中的 成员函
数 可以直接访问基类
中的public和
protected成员, 但不
能访问基类的private
成员。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-56-
main()
{
Rectangle rect;
cout<<rect.X; //?
cout<<rect.GetX();//??
}
z使用派生类的 对象
只能访问基类的
public成员
依然错误
!
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-57-
2.私有继承
私有继承的派生类定义形式:
class 派生类名: private 基类名
{
派生类新成员定义;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-58-
私有继承
private是定义私有继承方式的关键字
以私有继承方式定义的派生类,继承了基类中可以
继承的成员:公有成员、保护成员和私有成员,这些
成员在派生类中的访问属性都是私有的。
派生类的成员函数 可以访问基类的公有成员和保护
成员,不能访问基类的私有成员。
派生类以外的其它函数 则不能通过派生类的对象访
问从基类继承的任何成员。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-59-
class Point //基类声明
{
public:
void InitP(float xx=0, float yy=0)
{X=xx;Y=yy;}
void Move(float xOff, float yOff)
{X+=xOff;Y+=yOff;}
float GetX() {return X;}
float GetY() {return Y;}
private:
float X,Y;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-60-
class Rectangle: private Point //派生类声明
{public: //新增外部接口
void InitR(float x, float y, float w, float h)
{InitP(x,y);W=w;H=h;} //派生类访问基类公有成员
void Move(float xOff, float yOff) {Point::Move(xOff,yOff);}
float GetX() {return Point::GetX();}
float GetY() {return Point::GetY();}
float GetH() {return H;}
float GetW() {return W;}
private: //新增私有数据
float W,H;
};
派生类中的 成员函数
可以直接访问基类中
的public和protected
成员, 但不能访问基
类的private成员。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-61-
class Rectangle: private Point //派生类声明
{public: //新增外部接口
void InitR(float x, float y, float w, float h)
{
X=x; Y=y;//?
W=w;H=h;
}
void Move(float xOff, float yOff) {Point::Move(xOff,yOff);}
float GetX() {return Point::GetX();}
float GetY() {return Point::GetY();}
float GetH() {return H;}
float GetW() {return W;}
private: //新增私有数据
float W,H;
};
派生类中的 成员函数
可以直接访问基类中的
public和protected成
员, 但不能访问基类的
private成员。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-62-
class Point //基类声明
{
public:
void InitP(float xx=0, float yy=0)
{X=xx;Y=yy;}
void Move(float xOff, float yOff)
{X+=xOff;Y+=yOff;}
float GetX() {return X;}
float GetY() {return Y;}
protected:
float X,Y;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-63-
class Rectangle: private Point //派生类声明
{public: //新增外部接口
void InitR(float x, float y, float w, float h)
{
X=x; Y=y;//??
W=w;H=h;
}
void Move(float xOff, float yOff) {Point::Move(xOff,yOff);}
float GetX() {return Point::GetX();}
float GetY() {return Point::GetY();}
float GetH() {return H;}
float GetW() {return W;}
private: //新增私有数据
float W,H;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-64-
main()
{
Rectangle rect;
cout<<rect.X; //?
cout<<rect.GetX();//?
}
z使用派生类的 对象
不能访问基类中的任
何成员。
错误 !
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-65-
3. 保护继承
保护继承的派生类定义形式:
class 派生类名: protected 基类名
{
派生类新成员定义;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-66-
保护继承
protected是定义保护继承方式的关键字
以保护继承方式定义的派生类,继承了基类中可
以继承的成员:公有成员、保护成员和私有成
员。其中基类的公有成员和保护成员在派生类中
访问控制属性变成保护类型的,基类的私有成员
保持原来属性。
派生类的成员函数 可以访问基类的公有成员和保
护成员,不能访问基类的私有成员。
派生类以外的其它函数 则不能通过派生类的对象
访问从基类继承的任何成员。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-67-
class Point //基类声明
{
public:
void InitP(float xx=0, float yy=0)
{X=xx;Y=yy;}
void Move(float xOff, float yOff)
{X+=xOff;Y+=yOff;}
float GetX() {return X;}
float GetY() {return Y;}
private:
float X,Y;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-68-
class Rectangle: protected Point //派生类声明
{public: //新增外部接口
void InitR(float x, float y, float w, float h)
{InitP(x,y);W=w;H=h;} //派生类访问基类公有成员
void Move(float xOff, float yOff) {Point::Move(xOff,yOff);}
float GetX() {return Point::GetX();}
float GetY() {return Point::GetY();}
float GetH() {return H;}
float GetW() {return W;}
private: //新增私有数据
float W,H;
};
派生类中的 成员函数
可以直接访问基类中
的public和protected
成员, 但不能访问基
类的private成员。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-69-
class Rectangle: protected Point //派生类声明
{public: //新增外部接口
void InitR(float x, float y, float w, float h)
{
X=x; Y=y;//?
W=w;H=h;
}
void Move(float xOff, float yOff) {Point::Move(xOff,yOff);}
float GetX() {return Point::GetX();}
float GetY() {return Point::GetY();}
float GetH() {return H;}
float GetW() {return W;}
private: //新增私有数据
float W,H;
};
派生类中的 成员函数
可以直接访问基类中的
public和protected成
员, 但不能访问基类的
private成员。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-70-
class Point //基类声明
{
public:
void InitP(float xx=0, float yy=0)
{X=xx;Y=yy;}
void Move(float xOff, float yOff)
{X+=xOff;Y+=yOff;}
float GetX() {return X;}
float GetY() {return Y;}
protected:
float X,Y;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-71-
class Rectangle: protected Point //派生类声明
{public: //新增外部接口
void InitR(float x, float y, float w, float h)
{
X=x; Y=y;//??
W=w;H=h;
}
void Move(float xOff, float yOff) {Point::Move(xOff,yOff);}
float GetX() {return Point::GetX();}
float GetY() {return Point::GetY();}
float GetH() {return H;}
float GetW() {return W;}
private: //新增私有数据
float W,H;
};
派生类中的 成员函数
可以直接访问基类中的
public和protected成
员, 但不能访问基类的
private成员。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-72-
main()
{
Rectangle rect;
cout<<rect.X; //?
cout<<rect.GetX();//?
}
z使用派生类的 对象
不能访问基类中的任
何成员。
错误 !
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-73-
private private private private
private protected protected protected
private protected public public
private protected public
存取方式
继承类型
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-74-
9.3 派生类构造函数的定义
派生类继承了基类中除构造函数和析 构函 数之外
的所有成员。
基 类 的 构造函数和析构函数不能被派生类所继
承,派生类需要自己定义的构造函数和析构函数。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-75-
9.3.2 派生类的构造函数
派生类构造函数的一般形式 :
派生 类名: :派生 类名( 基类 所需 的形 参,本类成员所需的形参) :
基类1(基类参数表1), … ,基类n(基类参数表n),
对象成员1(对象参数表1), … ,对象成员m(对象参数表m)
{
本类基本类型数据成员初始化;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-76-
派生类的构造函数
单继承时的构造函数
派生类名::派生类名(基类所需的形参,本类成员所需的形参) :
基类名(参数)
{
本类成员初始化赋值语句;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-77-
class Point //基类 Point类的定义
{
public: //公有函数成员
Point(int xx=0, int yy=0) {X=xx;Y=yy;}
void InitP(int xx=0, int yy=0) {X=xx;Y=yy;}
void Move(int xOff, int yOff) {X+=xOff;Y+=yOff;}
int GetX() {return X;}
int GetY() {return Y;}
private: //私有数据成员
int X,Y;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-78-
class Rectangle: public Point
{
public:
Rectangle(int x, int y, int w, int h);
void InitR(int x, int y, int w, int h)
{InitP(x,y);W=w;H=h;} //派生类访问基类公有成员
void Move(int xOff, int yOff) {Point::Move(xOff,yOff);}
int GetX() {return Point::GetX();}
int GetY() {return Point::GetY();}
int GetH() {return H;}
int GetW() {return W;}
private: //新增私有数据
int W,H;
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-79-
Rectangle:: Rectangle(int x, int y, int w, int h): Point(x,y)
{
W=w;
H=h;
};
本类成员所需的形参
基类所需的形参
基类构
造函数
本类成员初始
化赋值语句;
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-80-
例9.5
定义一个派生类deriver,它是基类
base1和base2的多继承。Deriver类还有两
个私有的内嵌对象成员。定义派生类
deriver的构造函数。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-81-
class base1
{
private:
int m_base_data;
public:
base1(int data){m_base_data=data;}
//…
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-82-
class base2
{
private:
int m_base_data;
public:
base2(int data){m_base_data=data;}
//…
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-83-
class Abc
{
private:
float m_abc_data;
public:
Abc(float data){ m_abc_data=data; }
//…
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-84-
class deriver:public base1, public base2
{
private:
Abc m_member1, m_member2;
double m_deriver_data;
public:
deriver(int bd1, int bd2, float id1, float id2, double dd);
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-85-
deriver:: deriver(int bd1, int bd2, float id1, float id2,
double dd): base1(bd1),base2(bd2),
m_member1(id1), m_member2(id2)
{
m_deriver_data=dd;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-86-
使用基类无参构造函数
deriver:: deriver(float id1, float id2,
double dd): m_member1(id1), m_member2(id2)
{
m_deriver_data=dd;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-87-
使用对象数据成员的无参构造函数
deriver:: deriver(int bd1, int bd2, double dd):
base1(bd1),base2(bd2)
{
m_deriver_data=dd;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-88-
派生类的构造函数
如果基类和对象数据成员的构造函数都无参
数,派生类构造函数形参表中将只包含用于初
始化它自己的基本类型数据成员的参数。
如果这个派生类恰好没有基本类型的数据成
员,则其构造函数的形参表为空,可以不定义
构造函数,而使用系统提供的默认构造函数。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-89-
使用系统提供的默认构造函数
#include<iostream>
using namespace std;
class base
{
private:
int m_data;
public:
void SetData(int data){m_data=data;}
int GetData(){return m_data;}
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-90-
class deriver:public base
{
private:
int m_member;
public:
void SetMember(int m){ m_member=m;}
int GetMember(){return m_member;}
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-91-
void main()
{
int n(10);
deriver obj;
obj.SetMember(n);
cout<<obj.GetMember()<<endl;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-92-
派生类的构造函数
基类的构造函数不被继承,需要在派生类中自行
定义 。
定义构造函数时,以合适的初值为参数,初始化
本类中新增成员。
利用 成员初始化表 隐含调用 基类 和 新增对象数据
成员 的构造函数,初始化它们各自的数据成员。
构造函数的调用次序
? 系统会使用派生类构造函数的形参表的参数调
用基类和内嵌对象成员的构造函数。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-93-
派生类的构造函数
系统在 建立派生类 对象时,首先调用基类的构造
函数,再调用派生类的构造函数。
系统在 建立组合类 对象时,先调用内嵌子对象的
构造函数,在调用组合类的构造函数。
如果 一个派生类又是组合类 ,则系统先调用其基
类的构造函数,再调用其内嵌子对象的构造函
数,再后系统才调用派生类的构造函数。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-94-
派生类的构造函数
如果是 多继承 ,系统 调用基类构造函数的顺序 是
按照定义派生类时这些基类被继承的顺序进行
的,与这些基类构造函数在派生类构造函数成员
初始化列表的先后次序无关。
如果派生类有 多个对象数据成员 ,则系统 调用这
些对象数据成员的构造函数的顺序 是依据派生类
定义这些成员的顺序进行的,与派生类成员初始
化列表中对象数据成员构造函数排列先后次序无
关。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-95-
deriver:: deriver(int bd1, int bd2, float id1, float id2, double dd):
base2(bd2),base1(bd1), m_member2(id2) , m_member1(id1)
{
m_deriver_data=dd;
}
构造函数的
调用次序
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-96-
9.3.2 派生类的析构函数
派生类不能继承基类的析构函数,需要自己
定义析构函数,以便在派生类对象消亡之前
进行必要的清理工作。
派生类的析构函数只负责清理它新定义的非
对象数据成员,对象数据成员由对象成员所
属类的析构函数负责析构。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-97-
#include<iostream>
using namespace std;
class base
{
private:
int m_base_data;
public:
base(int data){m_base_data=data;}
~base(){cout<<"base object deconstruction"<<endl;}
//…
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-98-
class Abc
{
private:
float m_abc_data;
public:
Abc(float data){ m_abc_data=data; }
~Abc(){cout<<"Object member deconstruction"<<endl;}
//…
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-99-
class deriver:public base
{
private:
double m_deriver_data;
Abc m_member1;
int *m_ptr;
public:
deriver(int bd, float id, double dd);
~deriver();
void function();
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-100-
deriver:: deriver(int bd, float id, double dd):base(bd),
m_member1(id)
{
m_deriver_data=dd;
m_ptr=new int[256];
if(m_ptr==NULL)
cout<<"memory error in deriver obj"<<endl;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-101-
deriver::~deriver()
{
if(m_ptr!=NULL)
delete [] m_ptr;
cout<<"Deriver obj deconstruction."<<endl;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-102-
void deriver::function()
{
cout<<"Maybe you want to do something with m_ptr in this
function. "<<endl;
cout<<"Do as you like."<<endl;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-103-
void main()
{
int n(1);
float x(2.0f);
double d(3.0);
deriver obj(n,x,d);
obj.function();
cout<<"The end of main function"<<endl;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-104-
输出结果:
Maybe you want to do something with m_ptr in this function.
Do as you like.
The end of main function
Deriver obj deconstruction.
Object member deconstruction
base object deconstruction
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-105-
派生类的析构函数
如果没有特殊指针数据成员需要清理,可以使
用由系统提供的默认析构函数。
当派生类对象消亡时,系统调用析构函数的顺
序与建立派生类对象时调用构造函数的顺序正
好相反,即先调用派生类的析构函数,再调用
其对象数据成员的析构函数,最后调用基类的
析构函数。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-106-
9.4 多继承
9.4.1 多继承与二义性
多继承类结构中,派生类可能有多个直接基类
或间接基类,充分体现了软件重用的优点,但
也可能会引起成员访问的二义性或不确定性问
题。
例9.7 多继承时的二义性
#include<iostream>
using namespace std;
class base
{private:
int m_data;
public:
base(int m)
{ m_data=m;
cout<<"base construction"<<endl;
}
~base(){cout<<"base deconstruction"<<endl;}
void setdata(int data){m_data=data;}
int getdata(int data){ return m_data;}
};
class Fderiver1: public base
{
private:
int m_value;
public:
Fderiver1(int value,int data):base(data)
{ m_value=value;
cout<<"Fderiver1 construction"<<endl;
}
~Fderiver1(){cout<<"Fderiver1 deconstruction"<<endl;}
void setvalue(int value){ m_value=value;}
int getvalue(){ return m_value; }
void fun(){};
};
class Fderiver2: public base
{
private:
int m_number;
public:
Fderiver2(int number,int data):base(data)
{ m_number=number;
cout<<"Fderiver2 construction"<<endl;
}
~Fderiver2(){cout<<"Fderiver2 deconstruction"<<endl;}
void setnumber(int number){ m_number=number;}
int getnumber(){ return m_number; }
void fun(){};
};
class Sderiver: public Fderiver1, public Fderiver2
{private:
int m_attrib;
public:
Sderiver(int attrib,int number,int value,int data):
Fderiver1(value,data),Fderiver2(number,data)
{ m_attrib=attrib;
cout<<"Sderiver construction"<<endl;
}
~Sderiver(){cout<<"Sderiver deconstruction"<<endl;}
void setattrib(int attrib){m_attrib=attrib;}
int getattrib(){return m_attrib;}
void newfun1(){};
int newfun2(){};
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-111-
void main()
{
Sderiver object(3,4,5,6);
object.setdata(7);
}
产生二义性
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-112-
m_data
m_value
m_data
m_number
m_attrib
从
Fderiver1
继承
从
Fderiver2
继承
Sdreriver
自己声明的部分
图9 -4 多继承的派生类对象内存使用
base
Fderiver1 Fderiver2
Sderiver
图9- 3 多 继承类结构
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-113-
9.4.2 虚基类
为解决二义性问题,将共同基类设置为虚基类,创
建派生类对象时,虚基类的构造函数只会调用一
次,虚基类的成员在第三层派生类对象中就只有一
份拷贝,不会再引起二义性问题。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-114-
虚基类
语法形式:
class 派生类名: virtual 继承方式 基类名
{
//……
}
在多继承情况下,虚基 类关键字的作用范围和继承
方式关键字相同,只对紧随其后的基类起作用。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-115-
虚基类
在多继承类结构中,说明虚基类之后,虚基类的成
员在派生类中将不会因继承关系对虚基类的多次继
承而形成多份拷贝,只为最远的派生类提供唯一的
基类成员,消除了多继承结构中的二义性问题。
需要注意的是在第一级继承时就要将共同基类设计
为虚基类。
class Fderiver1: virtual public base
{
private:
int m_value;
public:
Fderiver1(int data,int value):base(data)
{ m_value=value;
cout<<"Fderiver1 construction"<<endl;
}
~Fderiver1(){cout<<"Fderiver1 deconstruction"<<endl;}
void setvalue(int value){ m_value=value;}
int getvalue(){ return m_value; }
void fun(){};
};
class Fderiver2: virtual public base
{
private:
int m_number;
public:
Fderiver2(int data, int number):base(data)
{
m_number=number;
cout<<"Fderiver2 construction"<<endl;
}
~Fderiver2(){cout<<"Fderiver2 deconstruction"<<endl;}
void setnumber(int number){ m_number=number;}
int getnumber(){ return m_number; }
void fun(){};
};
class Sderiver: public Fderiver1, public Fderiver2
{
private:
int m_attrib;
public:
Sderiver(int data, int value, int number, int attrib):
base(data),Fderiver1(data,value), Fderiver2(data,number)
{ m_attrib=attrib;
cout<<"Sderiver construction"<<endl;}
~Sderiver(){cout<<"Sderiver deconstruction"<<endl;}
void setattrib(int attrib){m_attrib=attrib;}
int getattrib(){return m_attrib;}
void newfun1(){};
int newfun2(){};
};
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-119-
多继承派生类构造函数的定义
在包含虚基类的继承结构中,系统在建立派生类的
对象时,调用构造函数的顺序是:
(1)首先按照虚拟基类被继承的顺序,调用它们的
构造函数;
(2)其次按照非虚拟基类被继承的顺序,调用它 们
的构造函数;
(3)再次按照对象数据成员声明的顺序,调用它 们
的构造函数;
(4)最后调用派生类自己的构造函数。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-120-
多继承派生类构造函数的定义
如果基类使用带参的构造函数,则派生类需要在其
构造函数的形式参数表中提供相应的参数给基类,对
其对象数据成员亦如此。
如果基类和对象数据成员都使用默认构造函数,派
生类也没有需要初始化的基本类型数据成员,也可以
使用默认构造函数。
析构派生类的对象时,析构函数的调用顺序正好与
构造函数的调用顺序相反。
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-121-
#include<iostream>
using namespace std;
void main()
{
int d (1), v (2), n (3), a (4);
Sderiver(d, v, n, a);
//add some function,…
cout<<"the end of main"<<endl;
}
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-122-
输出结果:
base construction
Fderiver1 construction
Fderiver2 construction
Sderiver construction
Sderiver deconstruction
Fderiver2 deconstruction
Fderiver1 deconstruction
base deconstruction
the end of main
2005-4-27
北京邮电大学电信工程学院计算机技术中心
-123-
作业