第 5章 程序的类层次结构
第 5章 程序的类层次结构
?类的继承与派生
?类层次中成员函数名的多态性
派生
?面向对象的程序设计的一个重要特点是允许
以既有类(也称基类),以其为基础导出
(定义)新的类(也称派生类)。这一过程
称为派生
?派生时不需要把既有类的相关代码重新书写
一遍,只需要指明是以哪个类为基类,便可
以将基类中的有关特征继承过来,实现了部
分代码的可重用。
类的继承与派生
?派生方式
?派生类的构造函数与释放函数
?多基派生
?虚基类
?类层次中成员名的作用域
?类层次中的类转换
派生方式
?public派生与 private派生
?Protected成员与 protected派生
public派生与 private派生
? C++允许程序员用下边的格式用一个类派生它的子
类,
class 派生类名, 派生方式 基类名
{
private,
新增私有成员声明语句表列
public,
新增公开成员声明语句表列
};
两种派生方式的特点
?1)无论哪种派生方式,基类中的 private成员
在派生类中都是不可见的。也就是说,基底
类中的 private成员不允许外部函数或派生类
中的任何成员访问。
?2) public派生时,基类中的 public成员相当
于派生类中的 public成员。
?3) private派生时,基类中的 public成员相当
于派生类中的 private成员
派生后基类成员访问性的变化
基类成员的
访问性
private public
派 生 方

private public private public
派 生 类 成

不可见 可见 可见
外 部 函

不可见 可见
定义 Location— Point类层次结构
class Location{ // 基类接口定义
public,
int mX_Pos,mY_Pos; // 位置的坐标,以像素点计
Location ( int x,int y); // 构造函数,初始化位置坐标
int getX ( ); // 返回当前位置的 x坐标
int getY ( ); // 返回当前位置的 y坐标
};
enum BOOLEAN { FALSE,TRUE }; // 定义一个布尔类型
class Point, private Location{ // 派生类接口定义
BOOLEAN mVisible; // 可见性变量
Public,
Point(int x,int y); // 构造函数,初始化点的位置
BOOLEAN isVisible ();// 返回当前点是否可见
Void show ( ); // 显示当前点
Void hide ( ); // 隐藏当前点
Void moveTo ( ); // 移动当前点
};
Protected成员与 protected派生
? protected成员是一种血缘关系内外有别的成员。它
只为它所在类中的方法和由它直接派生的类方法可

? private,protected,,public作为类成员的可见性修
饰符,将产生如下影响,
1) 在一个类中定义的方法函数,可以访问本类中的
任何成员,但只能访问基类中的 protected成员和
public成员;
2) 一个类对象,只能使用本类或其 public派生基类
中的 public成员
例 5.1.2
class b
{
protected,
int x1;
public,
int x2;
b() {x1 = x2 = 5;}
};
class d1:public b
{
public,
int fd11()
{
return x1; // ok,可以访问基类 protected成员
return x2; // ok,可以访问基类 public成员
}
int fd12()
{
b bb; // return bb.x1; 类对象不可使用本类 protected成员
return bb.x2; // ok,类对象可以使用基类 public成员
}
};
定义 Location— Point— Circle类层
次结构
class Location // 位置类
{
protected,// 保护类成员
int mX_Pos,mY_Pos; // 在 Location派生类中可访问
public,
Location ( int x,int y);
int getX ( );
int getY ( );
};
enum BOOLEAN { FALSE,TRUE };
class Point, public Location // 点类
{
protected,// 保护类成员
BOOLEAN mVisible; // 在 Point派生类中可访问
Public,
Point(int x,int y);
BOOLEAN isVisible ();
Void show ( );
Void hide ( );
Void moveTo ( );
};
class Circle,public Point // 圆类
{
protected,// 保护类成员
int mRadius; // 圆半径,在 Circle派生类中可访问
Public,
Circle(int x,int y,int r); // 初始化圆
void show ( ); // 画圆
void hide ( ); // 隐藏圆
void moveTo ( ); // 移动圆
void expand (int delta); // 放大圆,半径为( r + delta)
void contract (int delta); // 缩小圆,半径为( r - delta)
};
派生类中访问属性发生如下变化
? 1) 基类的 private成员在派生类中是不可见的
? 2) private派生使基类中的非 private成员都成为派生
类中的私有成员; protected派生使基类的非 private
成员在派生类中都变为 protected成员; public派生使
基类的非 private成员在派生类中访问属性保持不变
? 3) 不同的派生方式引起成员访问属性的改变,只能
降低,不能提高(如将 private成员提升为 protected
成员或 public成员)。但是,C++允许在派生类中用
访问属性修饰符,恢复一个成员原来的访问属性
例 5.1.4
class b
{
protected,
int x1;
int x2;
public,
int x3;
int x4;

};
class d:private b // x1,x2,x3,x4 将派生为 private成员
{
protected,
b::x1; // 将 x1恢复为 protected成员
public,
b::x3; // 将 x3恢复为 public成员

};
派生类的构造函数与释放函数
?类层次结构中数据成员的存储
?派生类中构造函数 /释放函数的定义与调用
?派生类对象的创建
类层次结构中数据成员的存储
?Location—Point—Circle类层次结构中数据成
员的存储
mX_Pos
mY_Pos
mX_Pos
mY_Pos
mVisible
mX_Pos
mY_Pos
mVisible
mRadius
构造函数 /释放函数的定义与调用
?首先通过派生类的构造函数调用基类的构造
函数,对基类成员进行初始化
?然后再执行对在派生类中新增的成员进行初
始化的操作
?因此可以说,派生类对象是由其所有先辈类
共同创建的
派生类的构造函数原型格式
Y::Y(argX1,argX2,…,argY1,argY2,…),X
(argX1,argX2,…)
?其中,X为 Y的直接基类名,Y为派生类名。
这一格式是递归的
类层次结构 Location—Point—Circle
中的构造函数
Location,,Location ( int x,int y)
{
mX_Pos = x;
mY_Pos = y;
}
Point,,Point ( int x,int y ), Location ( x,y)
// 先调用基类构造函数
{
visible = FALSE; // 缺省情况下是不可见的
}
Circle,,Circle ( int x,int y,int r ), Point ( x,y )
// 先调用基类构造函数
{
mRadius = r;
}
说明
? 1)创建一个 Circle类对象时,要自动调用 Point类的
构造函数,再由 Point类的构造函数调用其父类
Location构造函数
? 2)当一个派生类对象撤销时,释放函数调用的顺
序与构造函数相反
? 3)保护成员具有良好的继承性,对保护成员的初
始化最好在基类中进行
? 4)派生类的构造函数的构成形式,与包含对象成
员的类的构造函数相似但不相同
派生类对象的创建
?派生类对象可以用两种方式创建,
1)用常数参数表创建
2)由部分基类对象创建派生类对象
多基派生
?派生类只有一个基类时,称为单基派生。一
个派生类具有多个基类时,称为多基派生或
多重继承 (multiple inheritance),这时将继承
每个基类的部分代码。多基派生是单基派生
的扩展。与单基派生相比,既有同一性,又
有特殊性。单基派生则可以看成多基派生的
特例

?由 Hard(机器名)类与 Soft(软件,由 os与
Language组成)类派生出 System类
#include <iostream.h>
#include <string.h>
class Hard
{ protected,char bodyname[20];
public,Hard(char * bdnm) // 构造函数
{ cout<<"con H\n";
strcpy(bodyname,bdnm);
}
Hard(Hard & abody) // 复制构造函数
{ cout<<"copy H\n";
strcpy(bodyname,abody.bodyname);
}
void print()
{ cout<<"Body_name:"<<bodyname<<endl;
}
};
class Soft
{
protected,
char os[10];
char lang[15];
public,
Soft(char * o,char * lg) // 构造函数
{
cout<<"con F\n";
strcpy(os,o);
strcpy(lang,lg);
}
Soft(Soft & asoft) // 复制构造函数
{
cout<<"copy F\n";
strcpy(os,asoft.os);
strcpy(lang,asoft.lang);
}
void print()
{
cout<<"os:"<<os<<",language:"<<lang<<endl;
}
};
class System:public Hard,public Soft // 派生类 System
{
char owner[10];
public,
System(char * ow,char * bn,char * o,char * lg)
,Hard(bn),Soft(o,lg) // 调用基类构造函数
{
cout<<"con S\n";
strcpy(owner,ow);
}
System(Hard abody,Soft asoft,char * ow)
,Hard(abody),Soft(asoft) // 调用基类复制构造函数
{
cout<<"copy S\n";
strcpy(owner,ow);
}
void print()
{
cout<<"owner:"<<owner;
cout<<";\n hard:"<<bodyname;
cout<<";\n soft:"<<os<<","<<lang<<endl;
}
};
测试程序
void main()
{
System bsystem( "Wang",// 用常参数表创建派生类对象
"IBM PC",
"PC DOS",
"True BASIC");
bsystem.print();
cout<<"Ok!\n";[y1]
Hard abody("AST 386 sx/16");
Soft asoft("PC DOS","Borland C++");
System asystem( "AST 386sx/16",// 基类对象创建派生类对象
asoft,
"Zhang");
asystem.print();
}
虚基类
?在多基派生中,如果在多条继承路径上有一
个公共的基类(如图 5.5 (a) 中的 base0),
则在这些路径的汇合点(如图 5.5 (a) 中的
derived类对象),便会产生来自不同路径的
公共基类的多个拷贝,如图 5.5 (b) 所示。如
果只想保留公共基类的一个拷贝,就必须使
用关键字 virtual把这个公共基类定义为虚基类
在多条继承路径上公共基类的情形
base0
base1 base2
derived
base0{a} base0{a}
base1 base2
derived
在定义基类直接派生的类时说明格式
class 派生类名, virtual 派生方式 基类名 {

};
例 5.1.7
class base0
{
public,
int a;

};
class base1:virtual public base0
{

};
class base2:virtual public base0
{

};
class derived:public base1,public base2
{

};
几点说明
? 1) virtual也是“派生方式”中的一个关键字,它与
访问控制关键字( public或 private,protected)间
的先后顺序无关紧要
? 2)为了保证虚基类在派生类中只继承一次,就必
须将其直接派生类定义时都说明为虚拟派生;否则
除从用作虚基类的所有路径中得到一个拷贝外,还
从其它作为非虚基类的路径中各得到一个拷贝
? 3) 虚基类对象的初始化图 5.6a所示的类层次结构,
当 A与 B都是 C与 D的虚基类时,系统将要自左向右
按深度优先遍历算法对公共派生类 E进行初始化
虚基类对象的初始化
C
B A
D
E
A B A B
C D
E
类层次中成员名的作用域
? private成员的作用域只在本类对象内部
? public成员和 protected成员,在类层次结构中将形成成员名
多重作用域,
· 基类成员作用域和派生类新增成员作用域形成相包含的关
系,派生类新增成员作用域为内层,基类成员作用域在外层
· 内层声明的标识符可以覆盖外层声明的同名标识符,使其
不可见;对于多基派生类,如果其多个基类中拥有同名成员,
而在派生类中又新增了同名成员,则派生类的该同名成员将
覆盖所有基类的同名成员
· 如果在几个基类中具有同名成员,但在派生类中没有与它
们同名的新增成员名,则由于这些成员名具有相同的作用域,
使系统无法判定到底是调用哪个基类成员
有类层次结构:A ← B ← C
#include <iostream.h>
class A
{ public,
int x,a;
void fun(int i)
{ x = i;
cout << "A,,x = "<< x << endl;
cout << endl;
}
};
class B:public A
{ public,
int x,b;
void fun(int i)
{ x = i * a; // 引用非同名基类成员
cout << "B::x = "<< x << endl; // 引用本类成员
cout << "A::x = "<< A::x << endl; // 引用同名基成员
cout << "a = " << a << endl; // 引用非同名基成员
cout << endl;
}
};
class C:public B
{
public,
int x;
void fun(int i)
{
x = i * a * b; // 引用非同名基类变量
cout <<,C::x =, << x << endl; // 引用本类成员
cout <<,A::x =, << A::x <<,,B::x =, << B::x << endl;// 引用同名基类成员
cout <<,a =, << a <<,,b =, << b << endl; // 引用非同名基类成员
cout << endl;
}
};
测试程序
void main()
{
int i = 2;
C obj;
obj.a = 1; //引用非同名基成员
obj.A::fun(i); //引用同名基成员
obj.b = 2; //引用非同名基成员
obj.B::fun(i); //引用同名基成员
obj.fun(i); //引用派生类成员
}
例 5.1.9
class Base
{
public,
static void sb(); // 静态成员
void fb();
};
class D, private Base { }; // 全私有派生
class DD, public D
{
void fdd();
};
void DD,,fdd()
{
Base,,sb(); // 对
// fb(); 错
// sb(); 错
}
类层次中的类转换
?单基派生的情形
?多继承的情形
?含有公共虚基类的类层次结构
单基派生的情形
#include <iostream.h>
#include <string.h>
class Person
{ char * mName;
char mSex;
int mAge;
public,
Person (char *n="",int a=50,char s='m')
{ mName=new char[strlen(n)+1];
strcpy (mName,n);
mAge = a;
mSex = s;
}
~Person()
{ delete []mName;
}
void show ()
{ cout <<,\n name:” << mName;
cout <<,-age:” << mAge;
cout <<,-sex:” << mSex << endl;
}
Person& operator=(Person& p)
{
delete []mName;
mName=new char[strlen(p.mName)+1];
strcpy(mName,p.mName);
mAge=p.mAge;
mSex=p.mSex;
return *this;
}
};
class Employee, public Person
{
char * mDepartment;
float mSalary;
public,
Employee (char *,int,char,char *,float);
void show();
~Employee()
{
delete[]mDepartment;
}
};
Employee,,Employee (char * nm = "",int ag = 0,char sx = ' ',
char * dprt = "",float slr = 0)
, Person (nm,ag,sx)
{
mDepartment=new char[strlen(dprt)+1];
strcpy ( mDepartment,dprt);
mSalary = sly;
}
void Employee,,show()
{
Person,,print();
cout << "-department:" << department ;
cout << "-salary:" << salary << endl;
}
测试程序
void main()
{
Employee zh("Zhang",50,'m',"SHXIEMU",180);
Person c,* pP;
c =zh; // 1用派生类对象给基类对象赋值
c.show();
Person & y = zh; // 2用派生类对象初始化基类的引用
y.show();
pP = & zh; // 3把派生类对象地址赋值给基类指针
pP -> show();
((Employee *)pP) -> show(); // 4基类指针向派生类指针强制转换
}
多继承的情形
class base0
{
protected,
int b0;
};
class base1:public base0
{
protected,
int b1;
};
class base2:public base0
{
protected,
int b2;
};
class derived:public base1,public base2
{
float d;
public,
derived();
};
各层次数据成员在内存中的分布
Pb1
int b0
int b1
int b0
int b2

base0
base0
base1
base2
derived
Pb2
base0
base1 base2
derived
克服对基类成员访问的二义性
?对指针要显式地指明全路径
?将指针先强制转换到不会产生二义性的基类
?显式指明成员来自哪个类
含有公共虚基类的类层次结构
?使用虚基类,在它的几条派生路经的汇合处,
只产生其一个拷贝
?引用的都是同一个虚基类中的数据成员具有
相同的值。可以进行如下的类变换
1) 派生类对象的地址可以直接赋给间接公
基类的指针,并且不需进行强制类型转换
2) 一个虚基类的引用,可以引用一个派生
类的对象
类层次中成员函数名的多态性
?虚函数与动态绑定
?虚函数的访问
?纯虚函数与抽象类
?虚释放 函 数
?多基派生中虚函数的二义性
虚函数与动态绑定
?虚函数是用关键字 virtual修饰的某基类中的
protected或 public成员函数。它可以在派生
类中重定义,以形成不同的版本。只有在程
序执行过程中,依据指针具体指向哪个类对
象,或依据引用哪个类对象,才能确定激活
哪一个版本,实现动态绑定。即这种绑定方
式不是在编译时静态地进行,而必须在程序
运行过程中进行动态绑定
C++中静态多态性与动态多态性的主
要特征
形 式 绑定方式 各版本的函
数原型
绑定依据 特点
函数名重载 静态绑定 不同 参数数目及类型 高效
虚 函

动态绑定 相同 程序运行时引用或
指针指向
高灵活性、抽象性
和可扩充性
点 ─圆类层次结构 不使用虚函数
#include <iostream.h>
class Point // 基类
{ private,
float mX,mY;
public,
Point(){}
Point(float i,float j)
{ mX = i; mY = j; }
float area() // 非虚函数
{ return 0.0; }
};
const float Pi = 3.141593;
class Circle, public Point // 派生类
{ private,
float mRadius;
public,
Circle(float r)
{ mRadius = r; }
float area()
{ return Pi * mRadius * mRadius; }
};
点 ─圆类层次结构 使用虚函数
#include <iostream.h>
class Point
{ private,
float mX,mY;
public,
Point(){}
Point(float i,float j)
{ mX = i; mY = j; }
virtual float area() // 声明为虚函数
{ return 0.0; }
};
const float Pi = 3.141593;
class Circle:public Point
{ private,
float mRadius;
public,
Circle(float r)
{ mRadius = r; }
float area() // 虚函数的再定义
{ return Pi * mRadius * mRadius; }
};
虚函数声明与重定义的一般规则
? 1)在基类中,用关键字 virtual可以将其 public或 protected部
分的成员函数声明为虚函数
? 2)一个虚函数是属于它所在的类层次结构的,而不是只属
于某一个类,只不过它在该类层次结构中的不同类中具有不
同的形态
? 3) 如果派生类中没有对基类中说明的虚函数进行重定义,
则它继承基类中的虚函数
? 4) 由于有些编译器不能正确处理虚函数,因此要避免把构
造函数声明为虚函数
? 5) 虚函数只能是类的成员函数,不能把虚函数声明为静态
的或全局的;也不能把友元说明为虚函数。但是虚函数可以
是另一个类的友元函数
虚函数的访问
?用基指针访问与用对象名访问
?访问该类层次中的虚函数 ——要使用 this指针
?用构造函数/释放函数访问
用基指针访问与用对象名访问
?把一个函数声明为虚函数,保证了这个函数
被指向基类的指针(或引用)调用时,C++
系统对其进行动态绑定,向实际的对象传递
消息
?但是,通过一个对象名访问虚函数时,C++
系统将使用静态绑定
例 5.2.2
int main()
{
Circle c(6.4321);
Cout << c.area() << endl;
Cout << c.Point::area() << endl;
Cout << c.Circle::area() << endl;
}
这时,可以用作用域运算符指定使用的是哪个类的虚函数。这与非虚函数没有区别。
这里所说的“指向基类”是指指向最先将一个成员函数声明为虚函数的类。
例 5.2.3
#include <iostream.h>
class base0
{ public,void v() {cout << "base0\n";} // 非虚函数
};
class base1:public base0
{ public,
virtual void v() {cout << "base1\n";} // 声明虚函数
};
class A1:public base1
{ public,
void v() {cout << "A1\n";}
};
class A2:public A1
{ public,
void v() {cout << "A2\n";}
};
class B1:private base1
{ public,
void v() {cout << "B1\n";}
};
class B2:public B1
{ public,
void v() {cout << "B2\n";}
};
访问该类层次中的虚函数
#include <iostream.h>
class A
{
public,
virtual void v1(){cout << "v1 is called in A.\n";a1();}
void a1(){cout << "a1 is called in A.\n";v2();}
virtual void v2(){cout << "v2 is called in A.\n";}
};
class B:public A
{
public,
void b1(){cout << "b1 is called in B.\n";v2();}
virtual void v2(){cout << "v2 is called in B.\n";}
};
这里,this是一个指向基类 A的指针。由于 v2()是一个虚函数,因此 C++系统要通过
动态绑定实现,具体取决于所引用的对象
用构造函数/释放函数访问
#include <iostream.h>
class base
{ public,
base() {};
virtual void v1() { cout << "v1 is called in base.\n"; }
};
class A, public base
{ public,
A()
{ cout << " call v1 in A().\n";
v1(); // 调用本类中定义的虚函数
}
virtual void v1() {cout << "v1 is called in A.\n"; }
};
class B, public A
{ public,
B()
{ cout << "call v1 in B().\n";
A::v1(); // 调用基类中定义的虚函数
}
void v1() {cout << "v1 is called in B.\n";}
};
纯虚函数与抽象类
? 当在基类中不能为虚函数提供有实际意义的定义时,可以将
这个虚函数声明为纯虚函数
? 声明纯虚函数的形式为,
virtual 类型 函数名 (参数列表 ) = 0;
1)纯虚函数不能被直接调用,仅起提供一个与派生类相一致
的接口作用,用作派生类中的具有相同名字的函数存放处。
包含纯虚函数的类与把所有构造函数都声明为 protected成员
的类一样,也称为抽象类。一个抽象类只能作为基类来派生
新类,不能生成抽象类的对象。因此,当 Point被声明为抽象
类后,便不可生成 Point的对象
2)纯虚函数不可以被继承。当基类是抽象类时,在派生类中
必须给出基类中纯虚函数的定义,或在该类中再声明其为纯
虚函数
例 5.2.6
#include "figure.hpp"
const float Pi = 3.141593;
class circle:public figure
{ private,
float radius;
public,
circle(float r)
{ radius = r; }
float area() // 重定义
{ return radius * radius * Pi; }
};
class triangle:public figure
{ protected,
float high,wide;
public,
triangle(float h,float w)
{ high = h;
wide = w; }
float area() // 重定义
{ return high*wide*0.5; }
};
class rectangle:public triangle
{ public,
rectangle(float h,float w):triangle(h,w){} // tectangle类是 triangle类的派生类
float area() // 重定义
{ return high * wide; }
};
测试程序
#include <iostream.h>
#include "shape.h"
float total(figure *pf[],int n) // 求面积和
{
float sum = 0;
for(int i = 0; i < n; i ++)
sum += pf[i] -> area(); // 按实际对象调用 area()
return sum;
}
void main()
{
figure *pf[5]; // 指向抽象类的指针数组
//动态创建计算各种图形面积的对象元素
pf[0] = new triangle(3.0,4.0);
pf[1] = new rectangle(2.0,3.5);
pf[2] = new rectangle(5.0,1.0);
pf[3] = new rectangle(3.0,6.0);
pf[4] = new circle(10.0);
cout<<"total area:"<<total(pf,5)<<endl;
}
虚释放 函 数
?虽然构造函数不可以被声明为虚函数,但是
释放函数可以被声明为虚函数。一般说来,
如果一个类中定义了虚函数,释放函数也应
定义为虚释放函数,尤其是释放函数要完成
一些有意义的工作,如释放内存
例 5.2.7 通用迭代算子类
?对一种数据结构来说,迭代就是对其元素按
某种次序进行搜索的过程。这里,将按照标
准 C++技术,把迭代设计成为迭代算子类,
使其能具有通用性
?所谓通用性是指,
· 操作通用性
· 类型通用性
· 存储通用性
操作通用性
?对不同的数据结构有不同的迭代方法。操作
的通用性就是要抽象出共同方法。一般说来,
这些方法有,
· 构造函数,用以建立要被处理的数据元素;
· 请求所对应数据结构中的下一个元素;
· 判断迭代结束条件是否满足;
· 赋值运算符函数,复制构造函数和释放函数

class Int_iterator
{
int * mData; // 数组起始地址
int mLen; // 数组大小
public,
Int_iterator(int *,int); // 构造函数
int valid() const; // 是否可以请求下一个元素
int next(); // 取下一个元素
Int_iterator(const Int_iterator &);
Int_iterator &
Operator = (const Int_iterator &);
~Int_iterator();
};
构造函数中有两个参数:第一个用以指定数据结构的地址,第二个用以指定其中
元素的个数
类型通用性
template <class T>
class Iterator
{
T * mData;
int mLen;
public,
Iterator(T *p,int c)
,mData(p),mLen(c){}
int valid()const
{
return mLen > 0;
}
T next()
{
-- mLen;
return * mData ++;
}
};
sum函数模板
template <class T>
T sum(Iterator<T> ir)
{
T result = 0;
while (ir.valid())
result += ir.next();
return result;
}
存储通用性
?上面给出的 Iterator类只限于对存于数组中的
对象进行访问,因为其它的数据结构中的方
法函数的定义是不同的,数据元素也是不相
同的。但是,由于在( 1)中已经抽象出操作
方法的通用性,所不同的只是它们对不同数
据结构有不同的定义,因此可以把 Iterator类
定义成一个抽象基类,作为对不同数据结构
进行迭代的公共接口
定义如下
template <class T>
class Iterator
{
public,
virtual int valid() const = 0;
virtual T next() = 0;
virtual ~ Iterator() {}
};
多基派生中虚函数的二义性
?前面介绍过,多基派生中的多条路径具有公
共基类时,在这多条路径的汇合处就会因对
公共基类产生多个拷贝而产生同名函数调用
的二义性。消除这个二义性的办法是把公共
基类定义为虚基类,使由它派生的多条路径
的汇聚处只产生一个拷贝
例 5.2.8
#include <iostream.h>
class base
{
public,
virtual void a(){cout << "a() in base\n";}
virtual void b(){cout << "b() in base\n";}
virtual void c(){cout << "c() in base\n";}
virtual void d(){cout << "d() in base\n";}
virtual void e(){cout << "e() in base\n";}
virtual void f(){cout << "f() in base\n";}
};
class A:public base
{
public,
virtual void a(){cout << "a() in A\n";}
virtual void b(){cout << "b() in A\n";}
virtual void f(){cout << "f() in A\n";}
};
class B:public base
{
public,
virtual void a(){cout << "a() in B\n";}
virtual void b(){cout << "b() in B\n";}
virtual void c(){cout << "c() in B\n";}
};
class C:public A,public B
{
public,
virtual void a(){cout << "a() in C\n";}
virtual void d(){cout << "d() in C\n";}
};
例 5.2.9
#include "ex529.hpp"
void main()
{
C c;
base *pa = &c; // 指向公共基类的指针引用派生类对象
pa -> a();
pa -> b();
pa -> c();
pa -> d();
pa -> e();
pa -> f();
}
习题