1 / 60

第 7 章 继承与派生

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

quasar
Download Presentation

第 7 章 继承与派生

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


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

  2. 第7章 继承与派生 人类 教师类 管理人员类 学生类 教师管理人员类 7.1 类的继承与派生 人类:                    姓名、性别、年龄、身高、体重 教师类:                姓名、性别、年龄、身高、体重、专业、职称 管理人员类:         姓名、性别、年龄、身高、体重、职务 教师管理人员类: 姓名、性别、年龄、身高、体重、专业、职称、职务 学生类:                姓名、性别、年龄、身高、体重、学号、班级、专业 7.1.1 继承与派生的基本概念 在原有类的基础上派生出新的类,新类继承原有类的属性和方法,称原有的类为基类,新类称为派生类。 间接基类 直接基类 单继承:派生类 只有一个基类 多继承:派生类 有多个基类

  3. 第7章 继承与派生 7.1 类的继承与派生 7.1.2 派生类的声明 单继承派生类的声明语法为: class 派生类名: 继承方式基类名 { 派生类新增成员的声明; } 继承方式有三种(private,protected,public),在下一节中详细介绍。

  4. 第7章 继承与派生 例7.1 定义一个位置坐标类,属性有x坐标和y坐标,成员函数包括构造函数、获取x坐标的函数、获取y坐标的函数和移动位置到新的坐标点函数。然后定义派生类点类,除了继承基类(位置类)的成员外,又增加了新的数据成员颜色、获取颜色值的成员函数和显示数据成员值的函数。 #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(); };

  5. 第7章 继承与派生 例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指针有同样作用

  6. 第7章 继承与派生 例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 ; } 调用从CLocation类中继承来的成员函数GetX()和GetY()。

  7. 第7章 继承与派生 返 回 例7.1 (续三) 位置坐标通过CLocation类构造函数的默认参数值设置为(0,0) void main(void) { CPoint p("Red"); p.Show(); p.MoveTo(7,8); p.Show(); p.SetColor("Green"); p.Show(); } 程序运行结果为: 0,0 Red 7,8 Red 7,8 Green

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

  9. 第7章 继承与派生 7.2 类的继承方式 7.2.1 公有继承 公有继承的特点: (1) 基类的私有成员不能被派生类的函数成员访问 (2) 基类的公有成员和保护成员在派生类中的访问权限不变 分析例7.1 基类CLocation中的公有成员Getx()、Gety()和MoveTo(),在派生类CPoint中的访问权限仍然是公有的。 而x,y是基类CLocation的私有成员,在派生类CPoint的成员函数中不能访问。如果将CPoint类的成员函数Show()改写成下面的形式: void CPoint::Show() { cout << x << "," << y << " " << Color << endl ; } 错误信息:“不能访问CLocation类中的私有成员x,y”

  10. 第7章 继承与派生 7.2 类的继承方式 将x,y改为保护成员后,上面的show()就可以直接访问成员x和y了 7.2.1 公有继承(续一) 若将基类CLocation中的x,y改成保护成员,即: class CLocation { protected: int x; int y; public: int Getx(); int Gety(); void MoveTo(int x, int y); CLocation(int x=0, int y=0); };

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

  12. 第7章 继承与派生 7.2 类的继承方式 7.2.2 保护继承 保护继承的特点: (1) 基类的私有成员不能被派生类的函数成员访问 (2) 基类的公有成员和保护成员在派生类中的访问权限变为保护类型 例7.2 将例7.1的继承方式改为保护继承,其他代码保持不变 class CPoint :protected CLocation { private: char Color[10]; public: CPoint(char *c); void SetColor(char *c); void Show(); };

  13. 第7章 继承与派生 7.2 类的继承方式 错误:不能在主函数中访问CLocation类的函数MoveTo(),只能在派生类的成员函数中访问 7.2.2 保护继承(续一) 改为保护继承后,主函数不变编译时,是否正确? void main(void) { CPoint p("Red"); p.Show(); p.MoveTo(7,8); p.Show(); p.SetColor("Green"); p.Show(); }

  14. 第7章 继承与派生 7.2 类的继承方式 7.2.2 保护继承(续二) 为实现点的移动,可在CPoint类中再添加一个公有成员函数MoveTo(),如下: class CPoint:protected CLocation { private: char Color[10]; public: CPoint(char *c); void SetColor(char *c); void Show(); void MoveTo(int x, int y); }; void CPoint::MoveTo(int x, int y) { CLocation::MoveTo(x,y); } 用基类名和域运算符(::)可以访问基类的成员函数MoveTo(), 若不加限定,由于基类和派生类都有成员函数MoveTo(),将优先调用派生类本身的同名成员函数MoveTo()。

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

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

  17. 第7章 继承与派生 7.2 类的继承方式 7.2.3 私有继承 私有继承的特点: (1) 基类的私有成员不能被派生类的函数成员访问 (2) 基类的公有成员和保护成员在派生类中的访问权限变为私有类型 将例7.1的继承方式改为私有继承,其他代码保持不变 class CPoint :private CLocation { private: char Color[10]; public: CPoint(char *c); void SetColor(char *c); void Show(); };

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

  19. 第7章 继承与派生 7.2 类的继承方式 派生类继承基类成员访问权限的变化表:

  20. 第7章 继承与派生 例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; };

  21. 第7章 继承与派生 例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) //设置时间 { Hour = h; Minute = m; Second = s; } void Clock::ShowTime() //显示时间 { cout << Hour << ":" << Minute << ":" << Second << endl; }

  22. 第7章 继承与派生 例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; }

  23. 第7章 继承与派生 例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; }

  24. 第7章 继承与派生 返 回 例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(); } 构造函数采用缺省参数值,基类继承的数据成员初始化为(0,0,0),将AlermClock类自己定义的数据成员响铃时间置为(12,0,0) 程序运行结果为: 0 : 0 : 0 12 : 0 : 0 10 : 30 : 40 6 : 30 : 0

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

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

  27. 第7章 继承与派生 用初始化表的方式为a赋值,与函数体中使用 a=x 语句作用一样 用初始化表的方式为b、d赋值,为x和y初始化,调用基类A的构造函数为基类成员初始化 例7.4 派生类的构造过程 #include <iostream.h> class A { int a; public: A(int x):a(x) { cout << "construct A " << a << endl; } }; 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; } };

  28. 第7章 继承与派生 例7.4 (续) void main(void) { B b1(10); } 执行顺序: 先调用基类的构造函数,即执行初始化表中的A(v); 然后按着成员声明的先后先后顺序,应首先初始化b,即执行初始化表中的b(v),接下来是成员d,即执行初始化表中的d(b); 接下来构造内嵌对象x,即执行初始化表中的x(b+1)。再构造内嵌对象y,即执行初始化表中的y(b+2); 最后执行类B自己的构造函数体。 程序运行结果为: construct A 10 construct A 11 construct A 12 construct B 10 10 10

  29. 第7章 继承与派生 7.3 派生类的构造过程和析构过程 7.3.2 派生类的析构过程 派生类析构函数执行时将自动调用基类及内嵌对象的析构函数,因此不必显式调用。 派生类构造函数的执行顺序: (1)先执行派生类的析构函数。 (2)然后按着内嵌对象声明的相反顺序,依次调用内嵌对象的析构函数。 (3)最后调用基类的析构函数。

  30. 第7章 继承与派生 例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; } };

  31. 第7章 继承与派生 例7.5 派生类的析构顺序 程序运行结果为: construct A 10 construct A 11 construct A 12 construct B 10 10 10 destruct B 10 10 10 destruct A 12 destruct A 11 destruct A 10 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); }

  32. 第7章 继承与派生 例7.6 定义一个点类CPoint,数据成员有点的坐标,再定义一个几何形状类CShape,数据成员只有颜色,以CShape类为基类派生出线段类CLine和圆类CCircle,其中线段类CLine的数据成员包括起点和终点(为CPoint类的内嵌对象),圆类CCircle的数据成员包括圆心(为CPoint类的内嵌对象)和半径。 #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; } CPoint(Point &p) { X=p.X; Y=p.Y; } int GetX() { return X; } int GetY() { return Y; } };

  33. 第7章 继承与派生 例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; } };

  34. 第7章 继承与派生 输出颜色Color。因为基类的颜色Color是私有成员,在这里不能直接访问,只能通过它的公有函数PrintColor()访问 例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(); } };

  35. 第7章 继承与派生 例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(); } };

  36. 第7章 继承与派生 例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(); } 程序运行结果为: Draw a Shape. The color is Red Draw a Line from (10,10) to (100,100), with color Green Draw a Circle at center (50,50) with radius 20 and color Black

  37. 第7章 继承与派生 虽然父类的指针可以指向子类的对象,但调用的函数Draw()都是父类CShape的成员函数 返 回 例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(); } 程序运行结果为: Draw a Shape. The color is Red Draw a Shape. The color is Green Draw a Shape. The color is Red Black

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

  39. 第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; } };

  40. 第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; } };

  41. 第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; } };

  42. 第7章 继承与派生 构造函数执行顺序: 先调用基类CBase1构造函数,再调用基类CBase2构造函数,然后调用内嵌对象b1构造函数,再调用内嵌对象b2的构造函数,最后执行CDerived类本身的构造函数体。 析构过程与构造过程恰好相反。 例7.7 (续三) void main() { CDerived d1(1,2,3); } 程序运行结果为: Construct CBase1 1 Construct CBase2 2 Construct CBase1 2 Construct CBase2 3 Construct CDerived! 3 Denstruct CDerived! 3 Denstruct CBase2! 3 Denstruct CBase1! 2 Denstruct CBase2! 2 Denstruct CBase1! 1

  43. 第7章 继承与派生 类中继承了两次CBase0的成员 CBase0 CBase1 CBase2 CDerived 7.4 多继承 7.4.2 多继承的二意性 1. 基类有同名成员引起的二意性 多继承时,不同基类可能有同名成员,这样派生类中就可能有从不同基类继承的同名成员,在引用时产生二意性。 2.从多个路径继承同一个基类引起的二意性 多重继承时,低层的派生类有可能从不同的路径继承同一个基类的成员多次,引用这样的成员时也会产生二意性。

  44. 第7章 继承与派生 例7.8 多继承派生产生的二意性。 class CBase2 { protected: int b; public: CBase2(int x=0) { b=x; } int GetB() { return b; } }; #include <iostream.h> class CBase1 { protected: int b; public: CBase1(int x=0) { b=x; } int GetB() { return b; } };

  45. 第7章 继承与派生 对象d1的数据成员 1 2 3 CBase1::b CBase2::b d 例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 ambiguous d1.Output(); } 产生二意性,因为不知道访问哪一个基类继承来的b,可使用域运算符和基类名来限定,改为: CBase1::b或CBase2::b 产生二意性,改为:d1.CBase1::GetB()

  46. 第7章 继承与派生 例7.9 从多个路径继承同一个基类,引起的二意性。 class CBase1 : public CBase0 { public: CBase1(int x=0) : CBase0(x) { } }; class CBase2 : public CBase0 { public: CBase2(int x=0) : CBase0(x) { } }; #include <iostream.h> class CBase0 { protected: int b0; public: CBase0(int x=0) { b0=x; } int GetB0() { return b0; } };

  47. 第7章 继承与派生 b0 GetB0() CBase0 虽然可以通过域运算符与基类名解决 二意性问题,但一般情况下并不希望 派生类中有两份基类成员。 b0 GetB0() CBase1 b0 GetB0() CBase2 CBase1::b0 CBase2::b0 CBase1::GetB0() CBase2::GetB0() CDerived 例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; } 产生二意性,可使用域运算符和基类名来限定,改为: d1.CBase1::GetB0()或d1.CBase2::GetB0()

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

  49. 第7章 继承与派生 例7.10 虚基类的应用。 class CBase1 : virtual public CBase0 { public: CBase1(int x=0) : CBase0(x) { } }; class CBase2 : virtual public CBase0 { public: CBase2(int x=0) : CBase0(x) { } }; #include <iostream.h> class CBase0 { protected: int b0; public: CBase0(int x=0) { b0=x; } int GetB0() { return b0; } };

  50. 第7章 继承与派生 如果虚基类没有不带参数的构造函数,且有参数的 构造函数又没有默认参数值,则其所有的派生类(包 括间接派生类)的构造函数都必须为它的构造函数 提供参数。基类的数据成员b0得到的值是由最底层的 派生类CDerived的构造函数提供的。 例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;  } 只从基类CBase0中继承了一次GetB0(),因此直接调用不会产生二意性,也可以使用 d1.CBase1::GetB0()或d1.CBase2::GetB0() 调用同一个GetB0()函数 程序运行结果为: 10 10 10

More Related