60
7 7 第第第第第 第第第第第 7.1 7.1 第第第第第第第 第第第第第第第 7.2 7.2 第第第第第第 第第第第第第 7.3 7.3 第第第第第第第第第第第第第 第第第第第第第第第第第第第 7.4 7.4 第第第 第第第

第 7 章 继承与派生

  • Upload
    quasar

  • View
    170

  • Download
    6

Embed Size (px)

DESCRIPTION

第 7 章 继承与派生. 7.1 类的继承与派生 7.2 类的继承方式 7.3 派生类的构造过程和析构过程 7.4 多继承. 第 7 章 继承与派生. 人类. 教师类. 管理人员类. 学生类. 教师管理人员类. 7.1 类的继承与派生. 人类 :                    姓名、性别、年龄、身高、体重 教师类 :                姓名、性别、年龄、身高、体重、 专业、职称 管理人员类 :         姓名、性别、年龄、身高、体重、 职务 - PowerPoint PPT Presentation

Citation preview

Page 1: 第 7 章      继承与派生

第第 77 章 继承与派生章 继承与派生7.1 7.1 类的继承与派生类的继承与派生7.2 7.2 类的继承方式类的继承方式7.3 7.3 派生类的构造过程和析构过程派生类的构造过程和析构过程7.4 7.4 多继承多继承

Page 2: 第 7 章      继承与派生

7.1 7.1 类的继承与派生类的继承与派生7.1.1 7.1.1 继承与派生的基本概念继承与派生的基本概念 在原有类的基础上派生出新的类,新类继承原有类的属性和方在原有类的基础上派生出新的类,新类继承原有类的属性和方法,称原有的类为法,称原有的类为基类基类,新类称为,新类称为派生类派生类。。

第 7 章 继承与派生

人类人类: 姓名、性别、年龄、身高、体重: 姓名、性别、年龄、身高、体重教师类教师类: 姓名、性别、年龄、身高、体重、: 姓名、性别、年龄、身高、体重、专业、职称专业、职称管理人员类管理人员类: 姓名、性别、年龄、身高、体重、: 姓名、性别、年龄、身高、体重、职务职务教师管理人员类教师管理人员类: 姓名、性别、年龄、身高、体重、: 姓名、性别、年龄、身高、体重、专业、职称专业、职称、、职务职务学生类学生类: 姓名、性别、年龄、身高、体重: 姓名、性别、年龄、身高、体重、、学号、班级、专业学号、班级、专业

人类

教师类 学生类管理人员类

教师管理人员类 多继承:派生类有多个基类

单继承:派生类只有一个基类

直接基类间接基类

Page 3: 第 7 章      继承与派生

7.1 7.1 类的继承与派生类的继承与派生7.1.2 7.1.2 派生类的声明派生类的声明 单继承派生类的声明语法为:单继承派生类的声明语法为:

class class 派生类名派生类名 : : 继承方式继承方式    基类名基类名 {{                派生类新增成员的声明派生类新增成员的声明 ;; }}

              继承方式继承方式有三种(有三种( privateprivate ,, protectedprotected ,, publicpublic ),在下一),在下一节中详细介绍。节中详细介绍。

第 7 章 继承与派生

Page 4: 第 7 章      继承与派生

例例 7.1 7.1 定义一个位置坐标类,属性有定义一个位置坐标类,属性有 xx 坐标和坐标和 yy 坐标,成员坐标,成员函数包括构造函数、获取函数包括构造函数、获取 xx 坐标的函数、获取坐标的函数、获取 yy 坐标的函数和坐标的函数和移动位置到新的坐标点函数。然后定义派生类点类,除了继承移动位置到新的坐标点函数。然后定义派生类点类,除了继承基类(位置类)的成员外,又增加了新的数据成员颜色、获取基类(位置类)的成员外,又增加了新的数据成员颜色、获取颜色值的成员函数和显示数据成员值的函数。颜色值的成员函数和显示数据成员值的函数。#include <iostream.h>#include <string.h>class CLocation // 位置坐标类{private:

int x;int y;

public: CLocation(int x=0, int y=0); void MoveTo(int x, int y); int Getx();

int Gety();};

第 7 章 继承与派生

Page 5: 第 7 章      继承与派生

例例 7.1 7.1 (续一) (续一) CLocation::CLocation(int x, int y){

CLocation::x = x; // 可以写成 this->x = x;CLocation::y = y; // 可以写成 this->y = y;

}void CLocation::MoveTo(int x, int y){

CLocation::x = x; // 可以写成 this->x = x;CLocation::y = y; // 可以写成 this->y = y;

}int CLocation::Getx() {

return x;}int CLocation::Gety(){

return y;}

可通过域运算符( ::)访问类中的成员 x 和 y,与使用 this指针有同样作用

第 7 章 继承与派生

Page 6: 第 7 章      继承与派生

例例 7.1 7.1 (续二) (续二) class CPoint : public CLocation // 从 CLocation 中公有继承{private:

char Color[10];public:

CPoint(char *c);void SetColor(char *c);void Show();

};CPoint::CPoint(char *c){

strcpy(Color,c);}void CPoint::SetColor(char *c){

strcpy(Color,c);}void CPoint::Show(){

cout << Getx() << "," << Gety() << " " << Color << endl ;}

第 7 章 继承与派生

调用从 CLocation类中继承来的成员函数 GetX() 和 GetY()。

Page 7: 第 7 章      继承与派生

例例 7.1 7.1 (续三) (续三) void main(void)void main(void){{

CPoint p("Red");CPoint p("Red");p.Show();p.Show();p.MoveTo(7,8);p.MoveTo(7,8);p.Show();p.Show();p.SetColor("Green"); p.SetColor("Green"); p.Show();p.Show();

} }

位置坐标通过 CLocation类构造函数的默认参数值设置为( 0 , 0)

程序运行结果为:程序运行结果为:00 ,, 0 Red0 Red

77 ,, 8 Red8 Red

77 ,, 8 Green8 Green

第 7 章 继承与派生

返 回

Page 8: 第 7 章      继承与派生

7.2 7.2 类的继承方式类的继承方式类成员的访问权限:类成员的访问权限:私有成员私有成员 (private)(private) :可以被类自身的成员和友元访问,但不能被:可以被类自身的成员和友元访问,但不能被包括派生类在内的其他任何类和任何普通函数访问 包括派生类在内的其他任何类和任何普通函数访问 公有成员公有成员 (public)(public) :可以被任何普通函数和任何类的成员函数访问:可以被任何普通函数和任何类的成员函数访问保护成员保护成员 (protected)(protected) :可以被类自身的成员和友元访问外,还可:可以被类自身的成员和友元访问外,还可以被派生类的成员函数访问,但不能被任何非友元的普通函数访以被派生类的成员函数访问,但不能被任何非友元的普通函数访问 问 类的三种继承方式:类的三种继承方式:公有继承公有继承 (public)(public) ::保护继承保护继承 (protected)(protected) ::私有继承私有继承 (private)(private) ::

第 7 章 继承与派生

Page 9: 第 7 章      继承与派生

7.2 7.2 类的继承方式类的继承方式7.2.1 7.2.1 公有继承公有继承 公有继承的特点:公有继承的特点: (1) (1) 基类的基类的私有成员私有成员不能被派生类的函数成员访问不能被派生类的函数成员访问 (2) (2) 基类的基类的公有成员公有成员和和保护成员保护成员在派生类中的访问权限不变在派生类中的访问权限不变 分析例分析例 7.17.1 基类基类 CLocationCLocation 中的公有成员中的公有成员 GetxGetx()、()、 GetyGety()()和和MoveToMoveTo ()(),在派生类,在派生类 CPointCPoint 中的访问权限仍然是公有的。中的访问权限仍然是公有的。 而而 xx ,, yy 是基类是基类 CLocationCLocation 的私有成员,在派生类的私有成员,在派生类 CPointCPoint的成员函数中不能访问。如果将的成员函数中不能访问。如果将 CPointCPoint 类的成员函数类的成员函数 ShowShow ()()改写成下面的形式:改写成下面的形式: void CPoint::Show()void CPoint::Show() {{ cout << x << "," << y << " " << Color << endl ;cout << x << "," << y << " " << Color << endl ;

}}

第 7 章 继承与派生

错误信息:“不能访问 CLocation类中的私有成员 x , y”

Page 10: 第 7 章      继承与派生

7.2 7.2 类的继承方式类的继承方式7.2.1 7.2.1 公有继承(续一)公有继承(续一) 若将基类若将基类 CLocationCLocation 中的中的 xx ,, yy 改成保护成员,即:改成保护成员,即: class CLocationclass CLocation {{ protectedprotected:: int x;int x; int y;int y; public:public: int Getx();int Getx(); int Gety();int Gety(); void MoveTo(int x, int y);void MoveTo(int x, int y); CLocation(int x=0, int y=0);CLocation(int x=0, int y=0); };};

第 7 章 继承与派生

将 x , y改为保护成员后,上面的show()就可以直接访问成员 x 和 y了

Page 11: 第 7 章      继承与派生

7.2 7.2 类的继承方式类的继承方式7.2.1 7.2.1 公有继承(续二)公有继承(续二)分析例分析例 7.1CPoint7.1CPoint 类的部分成员属性:类的部分成员属性:CPointCPoint 类的成员函数类的成员函数不能访问的成员不能访问的成员有:有: int x; //int x; // 继承基类的私有成员,类的成员函数不可访问继承基类的私有成员,类的成员函数不可访问 int y; //int y; // 继承基类的私有成员,类的成员函数不可访问继承基类的私有成员,类的成员函数不可访问CPointCPoint 类的类的私有成员私有成员有:有: char Color[10]; //char Color[10]; // 自己定义的私有成员自己定义的私有成员CPointCPoint 类的类的公有成员公有成员有:有: int Getx(); //int Getx(); // 继承基类的公有成员,仍然是公有成员继承基类的公有成员,仍然是公有成员 int Gety(); //int Gety(); // 继承基类的公有成员,仍然是公有成员继承基类的公有成员,仍然是公有成员 void MoveTo(int x, int y); void MoveTo(int x, int y); //// 继承基类的公有成员,仍然是公有成员继承基类的公有成员,仍然是公有成员 void SetColor(char *c); //void SetColor(char *c); // 自己定义的公有成员自己定义的公有成员 void Show(); //void Show(); // 自己定义的公有成员自己定义的公有成员

第 7 章 继承与派生

Page 12: 第 7 章      继承与派生

7.2 7.2 类的继承方式类的继承方式7.2.2 7.2.2 保护继承保护继承 保护继承的特点:保护继承的特点: (1) (1) 基类的基类的私有成员私有成员不能被派生类的函数成员访问不能被派生类的函数成员访问 (2) (2) 基类的基类的公有成员公有成员和和保护成员保护成员在派生类中的访问权限变为保在派生类中的访问权限变为保护类型护类型例例 7.2 7.2 将例将例 7.17.1 的继承方式改为保护继承,其他代码保持不变的继承方式改为保护继承,其他代码保持不变 class CPoint class CPoint :: protectedprotected CLocation CLocation {{ private:private:

char Color[10];char Color[10]; public:public:

CPoint(char *c);CPoint(char *c); void SetColor(char *c);void SetColor(char *c); void Show();void Show();

};};

第 7 章 继承与派生

Page 13: 第 7 章      继承与派生

7.2 7.2 类的继承方式类的继承方式7.2.2 7.2.2 保护继承(续一)保护继承(续一) 改为保护继承后,主函数不变编译时,是否正确?改为保护继承后,主函数不变编译时,是否正确? void main(void)void main(void) {{

CPoint p("Red");CPoint p("Red");p.Show();p.Show();p.MoveTo(7,8); p.MoveTo(7,8); p.Show();p.Show();p.SetColor("Green"); p.SetColor("Green"); p.Show();p.Show();

}}

第 7 章 继承与派生

错误:不能在主函数中访问 CLocation类的函数MoveTo(),只能在派生类的成员函数中访问

Page 14: 第 7 章      继承与派生

7.2 7.2 类的继承方式类的继承方式7.2.2 7.2.2 保护继承(续二)保护继承(续二) 为实现点的移动,可在为实现点的移动,可在 CPointCPoint 类中再添加一个公有成员函数类中再添加一个公有成员函数MoveTo()MoveTo() ,如下:,如下: class CPoint:protected CLocationclass CPoint:protected CLocation {{ private:private:

char Color[10];char Color[10]; public:public:

CPoint(char *c);CPoint(char *c); void SetColor(char *c);void SetColor(char *c); void Show();void Show(); void MoveTo(int x, int y);void MoveTo(int x, int y);

};}; void CPoint::MoveTo(int x, int y)void CPoint::MoveTo(int x, int y) {{

CLocation::MoveTo(x,y);CLocation::MoveTo(x,y);

}}

第 7 章 继承与派生

用基类名和域运算符( ::)可以访问基类的成员函数MoveTo(),若不加限定,由于基类和派生类都有成员函数MoveTo(),将优先调用派生类本身的同名成员函数 MoveTo()。

Page 15: 第 7 章      继承与派生

7.2 7.2 类的继承方式类的继承方式7.2.2 7.2.2 保护继承(续三)保护继承(续三) 标识符的作用范围:标识符的作用范围:成员函数作用域成员函数作用域(如函数内部定义的局部(如函数内部定义的局部变量及函数参数),变量及函数参数),类或者派生类作用域类或者派生类作用域(如类中定义的数据成(如类中定义的数据成员或成员函数),员或成员函数),基类作用域基类作用域(如基类中定义的数据成员或成员(如基类中定义的数据成员或成员函数),函数),全局作用域全局作用域(如定义的全局变量,及全局函数,即普通(如定义的全局变量,及全局函数,即普通函数)。函数)。              标识符的访问原则:如果在某一程序段中有一个以上的同名标标识符的访问原则:如果在某一程序段中有一个以上的同名标识符都有效,则标识符的识符都有效,则标识符的作用范围越小,被访问到的优先级越高作用范围越小,被访问到的优先级越高。。 如果希望访问作用范围更大的标识符,则可以用类名和作用如果希望访问作用范围更大的标识符,则可以用类名和作用域域运算符运算符进行限定。如:进行限定。如: CLocation::MoveTo(x,y)CLocation::MoveTo(x,y) 。。

第 7 章 继承与派生

Page 16: 第 7 章      继承与派生

7.2 7.2 类的继承方式类的继承方式7.2.2 7.2.2 保护继承(续四)保护继承(续四)分析本例分析本例 CPointCPoint 类的部分成员属性:类的部分成员属性:CPointCPoint 类的成员函数类的成员函数不能访问的成员不能访问的成员有:有: int x; //int x; // 继承基类的私有成员,类的成员函数不可访问继承基类的私有成员,类的成员函数不可访问 int y; //int y; // 继承基类的私有成员,类的成员函数不可访问继承基类的私有成员,类的成员函数不可访问CPointCPoint 类的类的私有成员私有成员有:有: char Color[10]; //char Color[10]; // 自己定义的私有成员自己定义的私有成员CPointCPoint 类的类的保护成员保护成员有:有: int Getx(); //int Getx(); // 继承基类的公有成员,成为保护成员继承基类的公有成员,成为保护成员 int Gety(); //int Gety(); // 继承基类的公有成员,成为保护成员继承基类的公有成员,成为保护成员 void MoveTo(int x, int y); void MoveTo(int x, int y); //// 继承基类的公有成员,成为保护成员继承基类的公有成员,成为保护成员CPointCPoint 类的类的公有成员公有成员有:有: void MoveTo(int x, int y); //void MoveTo(int x, int y); // 自己定义的公有成员自己定义的公有成员 void SetColor(char *c); //void SetColor(char *c); // 自己定义的公有成员自己定义的公有成员 void Show(); //void Show(); // 自己定义的公有成员自己定义的公有成员

第 7 章 继承与派生

Page 17: 第 7 章      继承与派生

7.2 7.2 类的继承方式类的继承方式7.2.3 7.2.3 私有继承私有继承 私有继承的特点:私有继承的特点: (1) (1) 基类的基类的私有成员私有成员不能被派生类的函数成员访问不能被派生类的函数成员访问 (2) (2) 基类的基类的公有成员公有成员和和保护成员保护成员在派生类中的访问权限变为私在派生类中的访问权限变为私有类型有类型 将例将例 7.17.1 的继承方式改为私有继承,其他代码保持不变的继承方式改为私有继承,其他代码保持不变 class CPoint class CPoint :: privateprivate CLocation CLocation {{ private:private:

char Color[10];char Color[10]; public:public:

CPoint(char *c);CPoint(char *c); void SetColor(char *c);void SetColor(char *c); void Show();void Show();

};};

第 7 章 继承与派生

Page 18: 第 7 章      继承与派生

7.2 7.2 类的继承方式类的继承方式7.2.3 7.2.3 私有继承(续)私有继承(续)分析本例分析本例 CPointCPoint 类的部分成员属性:类的部分成员属性:CPointCPoint 类的成员函数类的成员函数不能访问的成员不能访问的成员有:有: int x; //int x; // 继承基类的私有成员,类的成员函数不可访问继承基类的私有成员,类的成员函数不可访问 int y; //int y; // 继承基类的私有成员,类的成员函数不可访问继承基类的私有成员,类的成员函数不可访问CPointCPoint 类的类的私有成员私有成员有:有: char Color[10]; //char Color[10]; // 自己定义的私有成员自己定义的私有成员 int Getx(); //int Getx(); // 继承基类的公有成员,成为私有成员继承基类的公有成员,成为私有成员 int Gety(); //int Gety(); // 继承基类的公有成员,成为私有成员继承基类的公有成员,成为私有成员 void MoveTo(int x, int y); void MoveTo(int x, int y); //// 继承基类的公有成员,成为私有成员继承基类的公有成员,成为私有成员CPointCPoint 类的类的公有成员公有成员有:有: void SetColor(char *c); //void SetColor(char *c); // 自己定义的公有成员自己定义的公有成员 void Show(); //void Show(); // 自己定义的公有成员自己定义的公有成员

第 7 章 继承与派生

Page 19: 第 7 章      继承与派生

7.2 7.2 类的继承方式类的继承方式派生类继承基类成员访问权限的变化表:派生类继承基类成员访问权限的变化表:

第 7 章 继承与派生

                派生控制基类成员 privateprivate protectedprotected publicpublic

privateprivate 不能访问不能访问 不能访问不能访问 不能访问不能访问protectedprotected privateprivate protectedprotected protectedprotected

publicpublic privateprivate protectedprotected publicpublic

Page 20: 第 7 章      继承与派生

例例 7.3 7.3 定义一个钟表类,数据成员有时、分、秒,成员函数定义一个钟表类,数据成员有时、分、秒,成员函数包括设置时间和显示时间。再从钟表类派生出闹钟类,新增数包括设置时间和显示时间。再从钟表类派生出闹钟类,新增数据成员有响铃时间,成员函数包括响铃、显示响铃时间和设置据成员有响铃时间,成员函数包括响铃、显示响铃时间和设置响铃时间。响铃时间。#include <iostream.h> class Clock { public:

Clock(int h=0, int m=0, int s=0);Clock(Clock &c);void SetTime(int h, int m, int s); void ShowTime();

private: int Hour;int Minute;int Second;

};

第 7 章 继承与派生

Page 21: 第 7 章      继承与派生

例例 7.3 7.3 (续一) (续一) Clock::Clock(int h, int m, int s) //构造函数的参数用于初始化时、分、秒{

Hour = h; Minute = m; Second = s;

} Clock::Clock(Clock &c) //拷贝构造函数将引用作为参数初始化新建对象{

Hour = c.Hour; Minute = c.Minute; Second = c.Second;

} void Clock::SetTime(int h, int m, int s) void Clock::SetTime(int h, int m, int s) ////设置时间设置时间{ {

Hour = h; Hour = h; Minute = m; Minute = m; Second = s; Second = s;

} } void Clock::ShowTime() void Clock::ShowTime() ////显示时间显示时间{ {

cout << Hour << ":" << Minute << ":" << Second << endl; cout << Hour << ":" << Minute << ":" << Second << endl; } }

第 7 章 继承与派生

Page 22: 第 7 章      继承与派生

例例 7.3 7.3 (续二) (续二) class AlermClock : public Clock{private:

int AlermHour;int AlermMinute;int AlermSecond;

public:AlermClock(int h=12, int m=0, int s=0);void Alerm();void SetAlermTime(int h, int m, int s);void ShowAlermTime();

};AlermClock::AlermClock(int h, int m, int s){

AlermHour = h; AlermMinute = m; AlermSecond = s;

}

第 7 章 继承与派生

Page 23: 第 7 章      继承与派生

例例 7.3 7.3 (续三) (续三) void AlermClock::Alerm() // 转义字符‘ \a’ 完成响铃{

cout << "\a\a\a\a\a\a\a";}void AlermClock::SetAlermTime(int h, int m, int s){

AlermHour = h; AlermMinute = m; AlermSecond = s;

}void AlermClock::ShowAlermTime(){

cout << AlermHour << ":" << AlermMinute << ":" << AlermSecond << endl;}

第 7 章 继承与派生

Page 24: 第 7 章      继承与派生

例例 7.3 7.3 (续四) (续四) void main() {

AlermClock c; c.ShowTime(); c.ShowAlermTime(); c.SetTime(10,30,40); c.SetAlermTime(6,30,0); c.ShowTime(); c.ShowAlermTime(); c.Alerm();

}

第 7 章 继承与派生

构造函数采用缺省参数值,基类继承的数据成员初始化为( 0 , 0 , 0),将AlermClock类自己定义的数据成员响铃时间置为( 12 , 0 , 0)

程序运行结果为:程序运行结果为:0 : 0 : 00 : 0 : 0

12 : 0 : 012 : 0 : 0

10 : 30 : 4010 : 30 : 40

6 : 30 : 06 : 30 : 0

返 回

Page 25: 第 7 章      继承与派生

7.3 7.3 派生类的构造过程和析构过程派生类的构造过程和析构过程 基类的构造函数和析构函数都不被继承,需要在派生类中重新基类的构造函数和析构函数都不被继承,需要在派生类中重新定义。由于派生类继承了基类的定义。由于派生类继承了基类的成员成员,在初始化时,也要同时初,在初始化时,也要同时初始化基类成员。可通过调用基类的构造函数对完成初始化。始化基类成员。可通过调用基类的构造函数对完成初始化。7.3.1 7.3.1 派生类的构造过程派生类的构造过程 派生类构造函数定义的一般格式为:派生类构造函数定义的一般格式为:派生类名派生类名 :::: 构造函数名(参数表):基类名(参数表),内嵌对构造函数名(参数表):基类名(参数表),内嵌对象象 11 (参数表(参数表 11 ),内嵌对象),内嵌对象 22 (参数表(参数表 22 ),),……,常量,常量 11 (初(初值值 11 ),常量),常量 22 (初值(初值 22 ),),……,引用,引用 11 (变量(变量 11 ),引用),引用 22(变量(变量 22 ))……{{

派生类构造函数体;派生类构造函数体;}}

第 7 章 继承与派生

Page 26: 第 7 章      继承与派生

7.3 7.3 派生类的构造过程和析构过程派生类的构造过程和析构过程7.3.1 7.3.1 派生类的构造过程(续)派生类的构造过程(续) 派生类构造函数的执行顺序:派生类构造函数的执行顺序: (( 11 )先调用基类的构造函数)先调用基类的构造函数 (( 22 )然后按照数据成员(包括内嵌对象、常量、引用等必须)然后按照数据成员(包括内嵌对象、常量、引用等必须初始化的成员)的声明顺序,依次调用数据成员的构造函数或初初始化的成员)的声明顺序,依次调用数据成员的构造函数或初始化数据成员始化数据成员 (( 33 )最后执行派生类构造函数的函数体)最后执行派生类构造函数的函数体注意注意::构造函数的执行顺序只与成员声明的顺序有关,而与构造函数的执行顺序只与成员声明的顺序有关,而与初始初始化表化表中各项的排列顺序无关。中各项的排列顺序无关。 常量成员、引用成员、内嵌对象常量成员、引用成员、内嵌对象,只能通过,只能通过初始化表初始化表的方法的方法初始化。初始化。

第 7 章 继承与派生

Page 27: 第 7 章      继承与派生

例例 7.4 7.4 派生类的构造过程派生类的构造过程#include <iostream.h>class A{ int a;public: A(int x):a(x)

{cout << "construct A " << a << endl;

}}; class B : public Aclass B : public A{{private:private: int b,c;int b,c; const int d;const int d; A x,y;A x,y;public:public: B(int v) : b(v), y(b+2), x(b+1), d(b), A(v)B(int v) : b(v), y(b+2), x(b+1), d(b), A(v)

{{ c = v;c = v; cout << "construct B " << b <<" " << c << " " << d << endl;cout << "construct B " << b <<" " << c << " " << d << endl; }}};};

第 7 章 继承与派生

用初始化表的方式为 a赋值,与函数体中使用  a=x 语句作用一样

用初始化表的方式为 b 、 d赋值,为 x 和 y初始化,调用基类 A的构造函数为基类成员初始化

Page 28: 第 7 章      继承与派生

例例 7.4 7.4 (续)(续)void main(void){

B b1(10);}

第 7 章 继承与派生

执行顺序:先调用基类的构造函数,即执行初始化表中的A(v);然后按着成员声明的先后先后顺序,应首先初始化 b,即执行初始化表中的 b(v),接下来是成员 d,即执行初始化表中的 d(b);接下来构造内嵌对象 x,即执行初始化表中的 x(b+1)。再构造内嵌对象 y,即执行初始化表中的 y(b+2);最后执行类 B自己的构造函数体。

程序运行结果为:程序运行结果为:construct A 10construct A 10

construct A 11construct A 11

construct A 12construct A 12

construct B 10 10 10construct B 10 10 10

Page 29: 第 7 章      继承与派生

7.3 7.3 派生类的构造过程和析构过程派生类的构造过程和析构过程7.3.2 7.3.2 派生类的析构过程派生类的析构过程 派生类析构函数执行时将自动调用基类及内嵌对象的析构函派生类析构函数执行时将自动调用基类及内嵌对象的析构函数,因此不必显式调用。数,因此不必显式调用。 派生类构造函数的执行顺序:派生类构造函数的执行顺序: (( 11 )先执行派生类的析构函数。)先执行派生类的析构函数。 (( 22 )然后按着内嵌对象声明的相反顺序,依次调用内嵌对象)然后按着内嵌对象声明的相反顺序,依次调用内嵌对象的析构函数。的析构函数。 (( 33 )最后调用基类的析构函数。)最后调用基类的析构函数。

第 7 章 继承与派生

Page 30: 第 7 章      继承与派生

例例 7.5 7.5 派生类的析构顺序派生类的析构顺序#include <iostream.h>class A{

int a;public:

A(int x):a(x){

cout << "construct A " << a << endl;}

~A(){

cout << "destruct A " << a << endl;}

};

第 7 章 继承与派生

Page 31: 第 7 章      继承与派生

例例 7.5 7.5 派生类的析构顺序派生类的析构顺序class B : public A{private:

int b,c;const int d;A x,y;

public:B(int v) : b(v), y(b+2), x(b+1), d(b), A(v){

c = v;cout << "construct B" << b <<" " << c << " " << d << endl;

}~B(){

cout << "desstruct B" << b <<" " << c << " " << d << endl;}

};void main(void){

B b1(10);}

第 7 章 继承与派生程序运行结果为:程序运行结果为:construct A 10construct A 10construct A 11construct A 11construct A 12construct A 12construct B 10 10 10construct B 10 10 10destruct B 10 10 10destruct B 10 10 10destruct A 12destruct A 12destruct A 11destruct A 11destruct A 10destruct A 10

Page 32: 第 7 章      继承与派生

例例 7.6 7.6 定义一个点类定义一个点类 CPointCPoint ,数据成员有点的坐标,再定义,数据成员有点的坐标,再定义一个几何形状类一个几何形状类 CShapeCShape ,数据成员只有颜色,以,数据成员只有颜色,以 CShapeCShape 类类为基类派生出线段类为基类派生出线段类 CLineCLine 和圆类和圆类 CCircleCCircle ,其中线段类,其中线段类CLineCLine 的数据成员包括起点和终点(为的数据成员包括起点和终点(为 CPointCPoint 类的内嵌对象),类的内嵌对象),圆类圆类 CCircleCCircle 的数据成员包括圆心(为的数据成员包括圆心(为 CPointCPoint 类的内嵌对象)类的内嵌对象)和半径。和半径。

#include <iostream.h>#include <string.h>class CPoint{private:

int X;int Y;

public:CPoint(int x=0, int y=0){

X=x;Y=y;

}

第 7 章 继承与派生

CPoint(Point &p){X=p.X;Y=p.Y;}int GetX(){return X;}int GetY(){return Y;}

};

Page 33: 第 7 章      继承与派生

例例 7.6 7.6 (续一)(续一)class CShape{private:

char Color[10];public:

CShape(char *c){

strcpy(Color,c);}void Draw(){

cout << "Draw a shape. The color is " << Color << endl;}void PrintColor(){

cout << Color << endl;}

};

第 7 章 继承与派生

Page 34: 第 7 章      继承与派生

例例 7.6 7.6 (续二)(续二)class CLine:public CShape{private:

CPoint Start; //线段的起点CPoint End; //线段的终点

public:CLine(CPoint s, CPoint e, char *c):CShape(c),Start(s),End(e){}void Draw(){

cout << "Draw a Line from (" << Start.GetX() << "," << Start.GetY();

cout << ") to ("<< End.GetX() << "," << End.GetY() << "), with color "; PrintColor();}

};

第 7 章 继承与派生

输出颜色 Color。因为基类的颜色 Color是私有成员,在这里不能直接访问,只能通过它的公有函数 PrintColor()访问

Page 35: 第 7 章      继承与派生

例例 7.6 7.6 (续三)(续三)class CCircle:public CShape{private:

CPoint Center; int Radius;

public:CCircle(CPoint ctr, int r, char *c):CShape(c),Center(ctr){

Radius = r;}void Draw(){

cout << "Draw a circle at center (" << Center.GetX() << "," ;cout << Center.GetY()<< ") with radius " << Radius << " and color

"; PrintColor();}

};

第 7 章 继承与派生

Page 36: 第 7 章      继承与派生

例例 7.6 7.6 (续四)(续四)void main(){

CShape s("Red");CPoint p1(10,10), p2(100,100),p3(50,50);CLine l(p1,p2,"Green");CCircle c(p3, 20, "Black");s.Draw();l.Draw();c.Draw();

}

第 7 章 继承与派生

程序运行结果为:程序运行结果为:Draw a Shape. The color is RedDraw a Shape. The color is Red

Draw a Line from (10,10) to (100,100), with color GreenDraw a Line from (10,10) to (100,100), with color Green

Draw a Circle at center (50,50) with radius 20 and color BlackDraw a Circle at center (50,50) with radius 20 and color Black

Page 37: 第 7 章      继承与派生

例例 7.6 7.6 (续五)(续五)父类与子类:如果派生类的派生控制为 public ,则这样的派生类称为基类的子类,而相应的基类则称为派生类的父类。 C++允许父类指针直接指向子类对象,也允许父类引用直接引用子类对象。

void main(){

CShape *ps[3];CShape s("Red");CPoint p1(10,10), p2(100,100),p3(50,50);CLine l(p1,p2,"Green");CCircle c(p3, 20, "Black");ps[0] = &s;ps[1] = &l;ps[2] = &c;for(int i=0; i<3; i++)

ps[i]->Draw();}}

第 7 章 继承与派生

程序运行结果为:程序运行结果为:Draw a Shape. The color is RedDraw a Shape. The color is RedDraw a Shape. The color is GreenDraw a Shape. The color is GreenDraw a Shape. The color is Red BlackDraw a Shape. The color is Red Black

虽然父类的指针可以指向子类的对象,但调用的函数 Draw()都是父类 CShape的成员函数

返 回

Page 38: 第 7 章      继承与派生

7.4 7.4 多继承多继承7.4.1 7.4.1 多继承的构造与析构多继承的构造与析构 一个派生类可以有多于一个的基类,称之为一个派生类可以有多于一个的基类,称之为多继承。多继承。 派生类构造函数的派生类构造函数的执行顺序执行顺序:: (( 11 )先按着声明的顺序(从左至右)依次调用各基类的构造)先按着声明的顺序(从左至右)依次调用各基类的构造函数。函数。 (( 22 )然后按照数据成员(包括内嵌对象、常量、引用等必须)然后按照数据成员(包括内嵌对象、常量、引用等必须初始化的成员)的声明顺序,依次调用数据成员的构造函数或初初始化的成员)的声明顺序,依次调用数据成员的构造函数或初始化数据成员。始化数据成员。 (( 33 )最后执行派生类构造函数的函数体。)最后执行派生类构造函数的函数体。 派生类派生类析构顺序析构顺序:: (( 11 )先执行派生类的析构函数。)先执行派生类的析构函数。 (( 22 )然后按着内嵌对象声明的相反顺序,依次调用内嵌对象)然后按着内嵌对象声明的相反顺序,依次调用内嵌对象的析构函数。的析构函数。 (( 33 )最后按基类声明的相反顺序调用各基类的析构函数。)最后按基类声明的相反顺序调用各基类的析构函数。

第 7 章 继承与派生

Page 39: 第 7 章      继承与派生

例例 7.7 7.7 多继承派生类的构造过程与析构过程。多继承派生类的构造过程与析构过程。#include <iostream.h>class CBase1 {protected:

int b;public:

CBase1(int x=0){

b=x;cout << "Construct CBase1! " << b <<endl;

}~CBase1(){

cout << "Destruct CBase1! " << b <<endl;}

};

第 7 章 继承与派生

Page 40: 第 7 章      继承与派生

例例 7.7 7.7 (续一)(续一)class CBase2 {protected:

int b;public:

CBase2(int x=0){

b=x;cout << "Construct CBase2! " << b <<endl;

}~CBase2(){

cout << "Destruct CBase2! " << b <<endl;}

};

第 7 章 继承与派生

Page 41: 第 7 章      继承与派生

例例 7.7 7.7 (续二)(续二)class CDerived : public CBase1,private CBase2 {protected:

CBase1 b1;CBase2 b2;int d;

public:CDerived(int x,int y, int z): :b1(y),CBase2(y),b2(z),CBase1(x){

d=z;cout << "Construct CDerived! " << d <<endl;

}~CDerived(){

cout << "Destruct CDerived! " << d <<endl;}

};

第 7 章 继承与派生

Page 42: 第 7 章      继承与派生

例例 7.7 7.7 (续三)(续三)void main(){

CDerived d1(1,2,3);}

第 7 章 继承与派生

程序运行结果为:程序运行结果为:Construct CBase1 1Construct CBase1 1

Construct CBase2 2Construct CBase2 2

Construct CBase1 2Construct CBase1 2

Construct CBase2 3Construct CBase2 3

Construct CDerived! 3Construct CDerived! 3

Denstruct CDerived! 3Denstruct CDerived! 3

Denstruct CBase2! 3Denstruct CBase2! 3

Denstruct CBase1! 2Denstruct CBase1! 2

Denstruct CBase2! 2Denstruct CBase2! 2

Denstruct CBase1! 1Denstruct CBase1! 1

构造函数执行顺序:先调用基类 CBase1构造函数,再调用基类 CBase2构造函数,然后调用内嵌对象b1构造函数,再调用内嵌对象 b2的构造函数,最后执行 CDerived类本身的构造函数体。析构过程与构造过程恰好相反。

Page 43: 第 7 章      继承与派生

7.4 7.4 多继承多继承7.4.2 7.4.2 多继承的二意性多继承的二意性 1. 1. 基类有同名成员引起的二意性基类有同名成员引起的二意性 多继承时,不同基类可能有同名成员,这样派生类中就可多继承时,不同基类可能有同名成员,这样派生类中就可能有从不同基类继承的同名成员,在引用时产生二意性。能有从不同基类继承的同名成员,在引用时产生二意性。 2.2. 从多个路径继承同一个基类引起的二意性从多个路径继承同一个基类引起的二意性 多重继承时,低层的派生类有可能从多重继承时,低层的派生类有可能从不同的路径继承同一个基不同的路径继承同一个基类的成员多次类的成员多次,引用这样的成员时也会产生二意性。,引用这样的成员时也会产生二意性。

第 7 章 继承与派生

CBase0

CBase1 CBase2

CDerived 类中继承了两次 CBase0的成员

Page 44: 第 7 章      继承与派生

例例 7.8 7.8 多继承派生产生的二意性。多继承派生产生的二意性。#include <iostream.h>class CBase1 {protected:

int b;public:

CBase1(int x=0){

b=x;}int GetB(){

return b;}

};

第 7 章 继承与派生

class CBase2 {protected:

int b;public:

CBase2(int x=0){b=x;}int GetB(){return b;}

};

Page 45: 第 7 章      继承与派生

例例 7.8 7.8 (续)(续)class CDerived : public CBase1,private CBase2 {protected:

int d;public:

CDerived(int x,int y, int z):CBase1(x),CBase2(y){

d=z;}void Output(){

cout << d << b << endl; // Error: CDerived::b' is ambiguous}

};void main(){

CDerived d1(1,2,3);int x = d1.GetB(); // Error: CDerived::GetB' is ambiguousd1.Output();

}

第 7 章 继承与派生

1

2

3

CBase1::bCBase2::b

d

对象 d1的数据成员

产生二意性,因为不知道访问哪一个基类继承来的 b,可使用域运算符和基类名来限定,改为:

CBase1::b 或 CBase2::b

产生二意性,改为:d1.CBase1::GetB()

Page 46: 第 7 章      继承与派生

例例 7.9 7.9 从多个路径继承同一个基类,引起的二意性。从多个路径继承同一个基类,引起的二意性。 #include <iostream.h>class CBase0 {protected:

int b0;public:

CBase0(int x=0){

b0=x;}int GetB0(){

return b0;}

};

第 7 章 继承与派生

class CBase1 : public CBase0 {public:

CBase1(int x=0) : CBase0(x){}

};class CBase2 : public CBase0 {public:

CBase2(int x=0) : CBase0(x){}

};

Page 47: 第 7 章      继承与派生

例例 7.9 7.9 (续)(续)class CDerived : public CBase1,public CBase2 {public:

CDerived(int x,int y):CBase1(x),CBase2(y){}

};void main(){

CDerived d1(1,2);cout << d1.GetB0() <<endl;

}

第 7 章 继承与派生

产生二意性,可使用域运算符和基类名来限定,改为:d1.CBase1::GetB0() 或 d1.CBase2::GetB0()

b0GetB0()

CBase0

CBase1 b0GetB0()

CBase2b0GetB0()

CDerivedCBase1::b0CBase2::b0

CBase1::GetB0()CBase2::GetB0()

虽然可以通过域运算符与基类名解决二意性问题,但一般情况下并不希望派生类中有两份基类成员。

Page 48: 第 7 章      继承与派生

7.4 7.4 多继承多继承7.4.3 7.4.3 虚基类虚基类 为了避免在派生类中从不同路径继承间接基类多次,可以通过为了避免在派生类中从不同路径继承间接基类多次,可以通过将间接基类声明为虚基类,虚基类的成员在它的间接派生类中只将间接基类声明为虚基类,虚基类的成员在它的间接派生类中只被继承一次。被继承一次。 1. 1. 虚基类的声明虚基类的声明 class class 派生类名 :派生类名 : virtualvirtual 继承方式 基类名继承方式 基类名

第 7 章 继承与派生

Page 49: 第 7 章      继承与派生

例例 7.10 7.10 虚基类的应用。虚基类的应用。 #include <iostream.h>class CBase0 {protected:

int b0;public:

CBase0(int x=0){

b0=x;}int GetB0(){

return b0;}

};

第 7 章 继承与派生

class CBase1 : virtual public CBase0 {public:

CBase1(int x=0) : CBase0(x){}

};class CBase2 : virtual public CBase0 {public:

CBase2(int x=0) : CBase0(x){}

};

Page 50: 第 7 章      继承与派生

例例 7.10 7.10 (续)(续)class CDerived : public CBase1,public CBase2 {public:

CDerived(int x,int y,int z):CBase0(x),CBase1(y),CBase2(z){}

};void main(){

CDerived d1(10,15,20);       cout << d1.GetB0() <<endl;         cout << d1.CBase1::GetB0() <<endl;         cout << d1.CBase2::GetB0() <<endl; }

第 7 章 继承与派生

只从基类 CBase0中继承了一次 GetB0(),因此直接调用不会产生二意性,也可以使用d1.CBase1::GetB0() 或

d1.CBase2::GetB0()调用同一个 GetB0()函数

如果虚基类没有不带参数的构造函数,且有参数的构造函数又没有默认参数值,则其所有的派生类(包括间接派生类)的构造函数都必须为它的构造函数提供参数。基类的数据成员 b0得到的值是由最底层的派生类 CDerived的构造函数提供的。

程序运行结果为:程序运行结果为:10 10

10 10

1010

Page 51: 第 7 章      继承与派生

7.4 7.4 多继承多继承7.4.3 7.4.3 虚基类(续)虚基类(续) 2. 2. 含有虚基类派生类的构造过程含有虚基类派生类的构造过程 派生类对象的派生类对象的构造顺序构造顺序:: (( 11 )按定义顺序自左至右地构造所有虚基类;)按定义顺序自左至右地构造所有虚基类; (( 22 )按定义顺序构造派生类的所有直接基类;)按定义顺序构造派生类的所有直接基类; (( 33 )按定义顺序构造派生类的所有数据成员,包括对象成员、)按定义顺序构造派生类的所有数据成员,包括对象成员、constconst 成员和引用成员;成员和引用成员; (( 44 )执行派生类自身的构造函数体。)执行派生类自身的构造函数体。 派生类对象的派生类对象的析构顺序析构顺序与构造顺序相反。与构造顺序相反。

第 7 章 继承与派生

Page 52: 第 7 章      继承与派生

例例 7.11 7.11 含有虚基类的派生类的构造过程。含有虚基类的派生类的构造过程。 #include <iostream.h>class CBase0 {protected:

int b0;public:

CBase0(int x){

b0 = x;cout << "construct CBase0 " << b0 << endl;

}}; class CBase1 : class CBase1 : virtualvirtual public CBase0 public CBase0 {{public:public:

CBase1(int x=0) :CBase1(int x=0) : CBase0(x)CBase0(x){{

cout << "construct CBase1 " << x << endl;cout << "construct CBase1 " << x << endl;}}

}; };

第 7 章 继承与派生

Page 53: 第 7 章      继承与派生

例例 7.11 7.11 (续一)(续一) class CBase2 : virtual public CBase0 {public:

CBase2(int x=0) : CBase0(x){

cout << "construct CBase2 " << x << endl;}

}; class CDerived : public CBase1,public CBase2 class CDerived : public CBase1,public CBase2 {{private:private:

CBase0 B0;CBase0 B0;CBase1 B1;CBase1 B1;CBase2 B2;CBase2 B2;

public:public:CDerived(int x,int y,int z,int a, int b, int c)CDerived(int x,int y,int z,int a, int b, int c)

::CBase0(x),CBase0(x),CBase1(y),CBase2(z),B0(a),B1(b),B2(c)CBase1(y),CBase2(z),B0(a),B1(b),B2(c){{

cout << "construct CDerived " << x <<"," << y << "," << z << endl;cout << "construct CDerived " << x <<"," << y << "," << z << endl;}}

};};

第 7 章 继承与派生

Page 54: 第 7 章      继承与派生

例例 7.11 7.11 (续二)(续二)void main(){

CDerived d1(10,15,20,25,30,35);}

第 7 章 继承与派生

在构造派生类的对象时,虚基类的成员只被初始化一次,且是由最底层派生类的构造函数通过调用虚基类的构造函数进行的,其他基类对虚基类构造函数的调用都被忽略。

程序运行结果为:程序运行结果为:construct CBase0 10construct CBase0 10

construct CBase1 15construct CBase1 15

construct CBase2 20construct CBase2 20

construct CBase0 25construct CBase0 25

construct CBase0 30construct CBase0 30

construct CBase1 30construct CBase1 30

construct CBase0 35construct CBase0 35

construct CBase2 35construct CBase2 35

construct CDerived 10,15,20construct CDerived 10,15,20

Page 55: 第 7 章      继承与派生

例例 7.12 7.12 含有虚基类的派生类的构造过程。以学校职工为基含有虚基类的派生类的构造过程。以学校职工为基类,派生出教师类和管理人员类,又从教师类和管理人员类共类,派生出教师类和管理人员类,又从教师类和管理人员类共同派生出教师管理人员类。 同派生出教师管理人员类。 #include <iostream.h>#include <string.h>class CStaff {protected:

int number;char name[10];int age;

public:CStaff(int num, char *na, int a){

number = num;age = a;strcpy(name, na);

}void Display(){

cout << name << " is a Staff " << age << " yeas old, " << endl;}

};

第 7 章 继承与派生

Page 56: 第 7 章      继承与派生

例例 7.12 7.12 (续一) (续一) class CTeacher : virtual public CStaff {protected:

char zch[10];public:

CTeacher(int num, char *na, int a, char *zc) : CStaff(num, na, a){

strcpy(zch,zc);}void Display(){

cout << name << " is a Teacher " << age << " yeas old, " << zch << endl;}

};

第 7 章 继承与派生

Page 57: 第 7 章      继承与派生

例例 7.12 7.12 (续二) (续二) class CManagement : virtual public CStaff {protected:

char zw[10];public:

CManagement(int num, char *na, int a, char *z) : CStaff(num, na, a){

strcpy(zw,z);}void Display(){

cout << name << " is a management " << age << " yeas old, " << zw << endl;}

};

第 7 章 继承与派生

Page 58: 第 7 章      继承与派生

例例 7.12 7.12 (续三) (续三) class CTeacherManagement : public CTeacher,public CManagement {public:

CTeacherManagement(int num, char *na, int a, char *zc,char *z): CStaff(num, na, a),CTeacher(num, na, a,zc),CManagement(num, na, a,z)

{}void Display(){

cout << name << " is a Teacher management " << age << " yeas old, " << zch << "," << zw << endl;

}};

第 7 章 继承与派生

不会产生二意性,因为只从虚基类 CStuff中继承了一次成员 name 和 age

Page 59: 第 7 章      继承与派生

例例 7.12 7.12 (续四) (续四) void main(){

CStaff s1(101,"Zhao",20);CTeacher t1(102,"Zhang",30,"Lecture");CManagement m1(103,"Wang",35,"dean");CTeacherManagement tm1(104,"Li",40, "Peofessor", "department head");s1.Display();t1.Display();m1.Display();tm1.Display();

}

第 7 章 继承与派生

程序运行结果为:程序运行结果为:Zhao is a Staff 20 years oldZhao is a Staff 20 years old

Zhang is a Teacher 30 years old, LectureZhang is a Teacher 30 years old, Lecture

Wang is a management 35 years old, deanWang is a management 35 years old, dean

Li is a Teacher management 40 years old, Peofessor, department headLi is a Teacher management 40 years old, Peofessor, department head

不会产生二意性,将优先调用自己类定义的 Display( ) 函数

返 回

Page 60: 第 7 章      继承与派生

谢 谢!谢 谢!