第四章 派生类与继承
1
4.1 派生类的概念
4.1.1 为什么要使用继承
从现有类出发建立新的类,使新类继承老类的特点和功能,并
且又加上自己的特点和功能的程序设计方法,是面向对象程序设
计一个重要的技术。
老的类叫 基类,新的类叫 派生类 。
对派生类可做如下几种变化,
(1) 可以增加新的成员数据。
(2) 可以增加新的成员函数。
(3) 可以重新定义己有的成员函数。
(4) 可以改变现有成员的属性。
C++中有两种继承,
单一继承 和 多重继承 。
对于单一继承
派生类只能有一个基类;
对于多重继承
派生类可以有多个基类。
昆 虫
有 翅 类 无 翅 类
蛾 苍蝇 蝴蝶 单一继承
继
承 派生
窗 口 滚 动 条
可滚动的窗口
多重继承
2
4.1.1为什么要使用继承?
class person{
private,
char name[10];
int age;
char sex;
public,
void print();
};
比较二个类
class employee{
private,
char name[10];
int age;
char sex;
char
department[20];
float salary;
public,
void print();
};
3
4.1.2 派生类的声明
基类,
class 类名
{
类的内容
};
派生方式可为公有派生 (用 public)和私有派生 (用 private或缺省 )。
class person{
private,
char name[10];
int age;
char sex;
public,
void print();
};
声明派生类,
class 派生类名, 派生方式 基类名
{
//派生类新增的数据成员和函数成员
};
class employee, public person {
private,
char department[20];
float salary;
public,
void print();
};
4
1,私有派生
(1) 私有派生类对基类成员的访问
基类的公有成员相当于派生类的私有成员,派生类的成员数
可以访问它。但基类的私有成员派生类的成员函数不可以访问它。
例 4.1
#include <iostream.h>
class base{ //定义基类
int x;
public,
void setx(int n) { x=n; }
void showx() { cout << x << endl; }
};
class derived, private base { //定义私有派生类
int y;
public,
void setxy(int n,int m)
{ setx(n); y=m; }
void showxy()
{ cout << x << y << endl; } // 非法 不能访问 x,改为 showx()
};
void main()
{ derived obj;
obj.setxy(10,20);
obj.showxy();
}
5
(2) 外部函数 (main)对私有派生类继承来的成员的访问
私有派生时,基类的所有成员在派生类中都成为私有成员,外部函
数不能访问。
例 4.2
#include <iostream.h>
class base{ //定义基类
int x;
public,
void setx(int n) { x=n; }
void showx() { cout << x << endl; }
};
class derived, private base { //定义私有派生类
int y;
public,
void sety(int n) { y=n; }
void showy()
{ cout << y << endl; }
};
void main()
{ derived obj;
obj.setx(10); //出错
obj.sety(20); //合法
obj.showx(); //出错
obj.showy(); //合法
}
6
2,公有派生
在公有派生中,基类的私有成员对派生类来说仍是基类的私有
成员,只能通过基类的公有成员函数访问 (相当于可访问性保持
不变 ),不允许外部函数和派生类的成员函数直接访问。基类中
的公有成员相当于派生类的公有成员,外部函数和派生类的成员
函数可直接访问。
例 4.3
#include <iostream.h>
class base{ //定义基类
int x;
public,
void setx(int n) { x=n; }
void showx() { cout << x << endl; }
};
class derived, public base {//定义公有派生类
int y;
public,
void sety(int n) { y=n; }
void showy()
{ cout << y << endl; }
};
void main()
{ derived obj;
obj.setx(10); //合法
obj.sety(20); //合法
obj.showx(); //合法
obj.showy(); //合法
}
7
在作类派生时,可能导致几个类使用同一函数名或变量名,这
就会产生二义性。
如 class X {
public,int f();
};
class Y, public X {
public,int f();
int g();
};
这里 b.f() 用的是 Y 类的 f() 还是 X 类的 f()? 用的是 Y 类的
f(),编译器会自动从继承底向上搜索,这叫作支配规则。如果一
定要用 X 类的 f(),则可写成 b.X::f(); X::叫作用域分辨符。
#include <iostream.h>
void main( )
{ X a;
Y b;
int i=a.f();
int j=b.f();
cout << "i=" << i << endl;
cout << "j=" << j << endl;
}
8
表 4.1公有派生和私有派生的访问特性
基类成员 基类私有成员 基类公有成员 基类保护成员
派生方式 p r i v a t e p u b l i c p r i v a t e p u b l i c p r i v a t e p u b l i c
派生类成员
外部函数
不可访问
不可访问
不可访问
不可访问
可访问
不可访问
可访问
可访问
可访问
不可访问
可访问
不可访问
9
4.1.3 基类的保护成员作用
无论私有派生还是公有派生,派生类无权访问它的基类的私有
成员。派生类要想访问它的基类的私有成员,只能通过调用基类
的成员函数的方式实现,对这有时会带来不便。C++还提供一
种访问属性 protected 保护性成员。基类的保护成员具有私有成
员和公有成员双重角色:对自已的对象而言它是私有成员,即不
能被其他程序或函数访问 (只能被类中的成员函数访问 );而对派
生类而言,它又相当于是基类的公有成员。当公有派生时,基类
的保护成员在派生类也为保护成员。当私有派生时,相当于派生
类的私有成员,派生类的成员函数可以访问它。
10
void main ()
{ samp obj(20,30);
obj.a=88; //非法,类的私有成员不能对外
obj.b=99; //非法,类的保护成员相当于私
有成员不能对外
obj.c=50 //合法,类的公有成员能对外
cout << obj.geta() << ' ';
cout << obj.getb() << ' ' <<obj.c<<endl;
}
例 4.4 保护成员的例子
#include <iostream.h>
class samp{
int a;
protected,
int b;
public,
int c;
samp(int n,int m )
{ a=n; b=m; } //构造函数
int geta() { return a; }
int getb() { return b; }
};
11
例 4.5 保护成员被公有派生后的例子
#include <iostream.h>
class base {
protected,
int a,b; //保护成员
public,
void setab(int n,int m) { a=n; b=m; }
};
class derive:public base {
int c;
public,
void setc(int n) { c=n; }
void showabc()//a,b 相当于派生类的私有成员,成员函数可访问
{ cout <<a<<' '<<b<<' '<<c<<endl; }
};
void main()
{ derive obj;
obj.setab(2,4); //基类的公有成员相当于派生类的公有成员,可被外
界访问
obj.setc(3);
obj.showabc();
}
12
例 4.6 保护成员被私有派生后的例子
#include <iostream.h>
class base {
protected,
int a; //保护成员
public,
void seta(int sa) { a=sa; }
};
class derive2:public derive1 {
int c;
public,
void setc(int sc) { c=sc; }
void show()
{ cout<<"a="<<a<<endl; //非法,保护成员 a 被 derive1私有派生后
成为 derive1的私有成员,dervie2中不能 //直接访问
cout<<“b=”<<b<<endl; //合法
cout<<“c=”<<c<<endl; } //合法
};
void main()
{ base op1;
op1.seta(1);
derive1 op2;
op2.setb(2);
derive2 op3;
op3.setc(3);
op3.show();
}
13
class derive1:private base {
protected,
int b;
public,
void setb(int sb) { b=sb; }
};
表 4.2 基类成员在派生类中和外部函数中的访问权限
派 生 方 式 基类中的 访问权限 派生类中的 访问权限
public
( 公有派生 )
public
protected
private
public
protected
private
private
( 私有派生 )
public
protected
private
private
private
14
保护成员的这种性质还可传播给派生的派生。
例 保护成员的性质传播给派生的派生的例子。
class Base
{ int value;
protected,void SetValue(int v) { value=v; }
};
class First, public Base
{ int total;
public,void SetTotal(int t)
{ total=t;
SetValue(t); //使用基类的保护成员,相当于本类的保护成员
}
}; class Second, public First
{ int count;
public,
void SetCount(int c)
{ count=c; SetValue(c); //使用基类的保护成员,相当于本类的保护成员
}
};
15
4.2 派生类的构造函数和析构函数
当一个派生类有构造函数时,则它必须对自己的数据成员和
继承而来的基类数据成员初始化,这和前面 (第三章 )讲的类定义
中有对象作为数据成员类似。因此需要由派生类的构造函数调用
基类的构造函数来完成。
16
4.2.1 派生类的构造函数和析构函数执行顺序
当创建派生类对象时,首先执行基类的构造函数,随后再执行
派生类的构造函数;当撤消派生类对象时,则先执行派生类的析
构函数,随后再执行基类的析构函数。
例 4.7
#include <iostream.h>
class base {
public,
base( ) { cout << "执行 base 类的构造函数 \n"; }
~base( ) { cout <<"执行 base 类的析构函数 \n"; }
};
class derive:public base {
public,
dervie( ):base( ) { cout << "执行 dervie 类的构造函数 \n"; }
~dervie( ) { cout <<"执行 dervie 类的析构函数 \n"; }
};
void main( )
{ dervie op;
}
17
4.2.2 派生类的构造函数和析构函数的定义规则
当派生类的基类构造函数含有参数时,必须定义派生类的构造函数。
格式为,
派生类的构造函数名 (参数表 ):基类的构造函数名 (参数表 )
{ //.....,} 其中,基类构造函数参数,来源于派生类的构造函
数的参数表。
例 4.8 #include <iostream.h> class base {
int i; public,
base(int n) //基类的构造函数 { cout << "执行 base 基类的构造函数 \n";
i=n; }
~base( ) { cout <<"执行 base 基类的析构函数 \n"; } void showi( ) { cout << i << endl; }
}; class derive:public base {
int j; public,
dervie(int n,int m):base(m) //派生类的构造函数 { cout << "执行 dervie 派生类的构造函数 \n";
j=n; }
~dervie( ) { cout <<"执行 dervie 派生类的析构函数 \n"; } void showj( ) { cout << j << endl; }
};
void main( )
{ derive
obj(30,40);
obj.showi( );
obj.showj( );
}
18
当派生类中还含有对象成员时,其构造函数的格式为,
派生类的构造函数名 (参数表 ):基类的构造函数名 (参数表 ),对象成员
1(参数表 ),...,
对象成员 n(参数表 ) { //.....,}
定义派生类对象时构造函数执行顺序,
基类的构造函数 对象成员构造函数 派生类的构造函数;
当撤消派生类对象时,析构函数执行顺序正好相反。 例 4.9#include <iostream.h>
class base { int x;
public,base(int i) //基类的构造函数
{ cout << "执行 base 基类的构造函数 \n"; x=i;
} ~base() { cout <<"执行 base 基类的析构函数 \n"; }
void show() { cout << "x=" << x << endl; } };
class derive:public base { base d;
public, dervie(int i):base(i),d(i) //派生类的构造函数
{ cout << "执行 dervie 派生类的构造函数 \n"; } ~dervie() { cout <<"执行 dervie 派生类的析构函数 \n"; }
};
void main()
{ derive
obj(5);
obj.show();
}
说明 P-105
19
4.3 多重继承
从多个类派生出一个类,也即一个类从多个基类继承称为多继承。
4.3.1 多重继承的声明
多重继承的声明格式如下,
class 派生类名, 派生方式 1 基类名 1,...,派生方式 n 基类名 n
{
//派生类新增的数据成员和函数成员
};
派生类继承了基类 1 到 基类 n的所有数据成员和函数成员,其访问
权限规则与单继承情况一样,多继承可看成单一继承的扩展。
例 4.10
#include <iostream.h>
class Y {
int b;
public,
void setY(int x) { b=x; }
void showY()
{ cout <<"b="<<b<<endl; }
};
class X {
int a;
public,
void setX(int x) { a=x; }
void showX()
{ cout <<"a="<<a<<endl; }
};
20
class Z,public X,private Y {
int c;
public,
void setZ(int x,int y) { c=x; setY(y); }
void showZ() { showY(); cout<<"c="<<c<<endl; }
// void setZ(int x,int y,int z) { a=x; setY(y); c=z; }
// void showZabc() { cout<<"a="<<a<<endl;
// showY(); cout<<"c="<<c<<endl; }
};
void main()
{ Z obj;
obj.setX(3);
obj.showX();
obj.setY(4); //错误
obj.showY(); //错误
obj.setZ(6,8);
obj.showZ();
// obj.setZ(1,2,3);
// obj.showZabc();
}
21
再讲一个例
class A {
private,
int a;
public,
void setA(int);
void showA( );
};
class B {
private,
int b;
public,
void setB(int);
void showB( );
};
A类的公有成员 (和保护成员 ) C类中仍是公有的 (保护的 )
B类所有成员 C类中是私有的。
class C:public A,private B {
private,
int c;
public,
void setC(int,int);
void showC( );
};
22
完整的程序如下,
#include <iostream.h>
class A {
private,
int a;
public,
void setA(int x){ a=x; }
void showA(){ cout << a <<endl; }
};
class B {
private,
int b;
public,
void setB(int x){ b=x; }
void showB(){ cout << b <<endl; }
};
23
class C:public A,private B {
private,
int c;
public,
void setC(int x,int y)
{ c=x; setB(y); }
void showC( )
{ showB( ); cout<< c <<endl; }
};
void main()
{ C obj;
obj.setA(3);
obj.showA();
obj.setB(5); //错误
obj.showB(); //错误
obj.setC(5,8);
obj.showC();
}
24
真正生产二义性的情况是,一个派生类从多个基类派生,而这些基
类又有一个相同的 基类,则在派生类访问这个共同基类的成员时
会产生二义性。
class A {
public,
void fun() ;
};
class B {
public,
void fun() ;
void gun() ;
};
class C,public A,public B {
void gun() ;
void hun() ;
}
若有对象 C obj ; obj.fun() 是使用 A 类的 fun() 呢? 也是
B 类的 fun()? 编译器无法自动确定,因为对 C 类来说 A 类和 B
类是同一级,我们可以用成员名限定 (作用域分辨操作符 )来人为
的消去二义性。
obj.A::fun(); obj.B::fun();
25
4.3.2 多重继承的构造函数和析构函数
设类 Y从 X1,X2,....Xn类派生 (当只有一个 X1时为单继承,否则
为多继承 ),则派生类 Y的构造函数 Y的一般形式为,
Y::Y(参数表 0):X1(参数表 1),X2(参数表 2),.....,Xn(参数表 n)
{ //… }
其调用顺序为先 X1,X2,...,Xn(从左向右 ),最后为 Y
析构函数的执行顺序和构造函数正好向反。
例 4.11
#include <iostream.h>
class X {
int a;
public,
X(int sa) { a=sa; } //X类构造函数
int getX() { return a; }
};
class Y {
int b;
public,
Y(int sb) { b=sb; } //Y类构造函数
int getY() { return b; }
};
26
class Z:public X,private Y {
int c;
public,
Z(int sa,int sb,int sc):X(sa),Y(sb)
{ c=sc; } //Z类构造函数
int getZ( ) { return c; }
int getY( ) { return Y::getY( ); }
};
void main()
{ Z obj(2,4,6);
int ma=obj.getX(); cout << "a=" << ma << endl;
int mb=obj.getY(); cout << "b=" << mb << endl;
int mc=obj.getZ(); cout << "c=" << mc << endl;
}
27
4.3.3 虚基类
1,为什么要引入虚基类。
一个派生类从多个基类派生,而这些基类又有一个相同的基类,
则
在派生类访问这个共同基类的成员时会产生二义性。虽然我们可以
用“作用域分辨操作符”区别,但这时基类的成员有二个副本 (拷
贝 )。
如,
class base {
public,
int b;
};
class base1:public base { };
class base2:public base { };
class derived,public base1,public base2 {
public,
int fun() { }
};
程序运行结果是
path base1==>8
path base2==>15
void main( )
{ derived d;
d.base1::b=8;
d.base2::b=15;
cout << "path
base1==>"<<d.base1::b<<endl;
cout << "path
base2==>”<<d.base2::b<<endl;
}
28
base base
base1 base2
derived
2,虚基类的概念
如果希望基类只有一个拷贝,则在定义时把基类说明成虚基类,
加上 virtual,
class base {
public,
int b;
};
class base1:virtual public base { };
class base2:virtual public base { };
class derived,public base1,public base2 {
public,
int fun() { }
};
这时程序运行结果是
path base1==>15
path base2==>15
这时也没有了二义性,可以写成 int i=d.b;
void main( )
{ derived d;
d.base1::b=8;
d.base2::b=15;
cout << "path
base1==>”<<d.base1::b<<endl;
cout << "path
base2==>”<<d.base2::b<<endl;
}
29
例 4.12
#include <iostream.h>
class base{
protected,
int a;
public,
base ( ) { a=5; }
};
class base1:public base {
public,
base1( ) { cout <<"base1 a="<<a<<endl; }
};
class base2:public base {
public,
base2( ) { cout <<"base2 a="<<a<<endl; }
};
class deeived, public base1,public base2 {
public,
derived( ) { cout <<"derived a="<<a<<endl; }
};
void main( )
{
derived obj;
}
30
例 4.13
#include <iostream.h>
class base{
protected,
int a;
public,
base() { a=5; }
};
class base1:virtual public base {
public,
base1() { cout <<"base1 a="<<a<<endl; }
};
class base2:virtual public base {
public,
base2() { cout <<"base2 a="<<a<<endl; }
};
class deeived, public base1,public base2 {
public,
derived() { cout <<"derived a="<<a<<endl; }
};
void main()
{
derived obj;
}
31
3,虚基类的初始化
调用各类构造函数的顺序原则如下,
(1) 同一层次中先调用虚基类的构造函数 (不论它在何处 ),再是非
虚基类。
(2) 若有多个虚基类在同一层次,则按它们声明的次序。
(3) 若虚基类前又由非虚基类派生而来,则仍先非虚基类,再这
虚基类。
class A {,..,};
class B {,..,};
class C,virtual public A,virtual public B {....};
class D,virtual public B,virtual public A {....};
class E,public C,public D {,.....,};
则建立 E 的对象时,
先 C 的 A 构造函数,C 的 B, C
再 D ( D 的 A,B是虚只一个副本 C 中已做 )
最后是 E
32
例 4.14
#include <iostream.h>
class base {
int a;
public,
base(int sa) //base类构造函数
{ a=sa; cout<<"构造 base 类 "<<endl; }
};
class base1:virtual public base {
int b;
public,
base1(int sa,int sb):base(sa) //base1类构造函数
{ b=sb; cout<<"构造 base1 类 "<<endl; }
};
class base2:virtual public base {
int c;
public,
base2(int sa,int sc):base(sa) //base2类构造函数
{ c=sc; cout<<"构造 base2 类 "<<endl; }
};
33
续上 例 4.14
class derived:public base1,public base2{
int d;
public,
derived(int sa,int sb,int sc,int sd),
base(sa),base1(sa,sb),base2(sa,ac) //derived类构造函数
{ d=sd; cout<<"构造 derived 类 "<<endl; }
};
void main()
{
derived obj(2,4,6,8);
}
34
4.4 应用举例
例 4.4.1 单继承
#include <iostream.h>
class base {
private,
int a;
public,
base(int i)
{ a=i; cout << "构造 base" << endl; }
~base()
{ cout << "析放 base" <<endl; }
};
class derived:public base {
private,
int d;
public,
derived(int i,int j):base(i) //derived 构造函数
{ d=j; cout << "构造 derived" << endl; }
~derived() //derived 析构函数
{ cout << "析放 derived" <<endl; }
};
35
续上 例 4.4.1
/* class derived:public base {
private,
int d;
base member; //对象作为数据成员
public,
derived(int i,int j,int k):base(i),member(j) //derived 构造函数
{ d=k; cout << "构造 derived" << endl; }
~derived() //derived 析构函数
{ cout << "析放 derived" <<endl; }
};
*/
void main( )
{
derived d(5,8);
}
36
例 4.4.2 多层单继承
#include <iostream.h>
class base1 {
private,
int a;
public,
base1(int i)
{ a=i; cout << "构造 base1" << endl; }
~base1()
{ cout << "析放 base1" <<endl; }
};
class base2:public base1 {
private,
int b;
public,
base2(int i,int j):base1(j)
{ b=i; cout << "构造 base2" << endl; }
~base2()
{ cout << "析放 base2" <<endl; }
};
37
续上 例 4.4.2
class derived:public base2 {
private,
int d;
public,
derived(int i,int j,int k):base2(i,j) //derived 构造函数
{ d=k; cout << "构造 derived" << endl; }
~derived() //derived 析构函数
{ cout << "析放 derived" <<endl; }
};
void main( )
{
derived d(5,8,9);
}
38
例 4.4.3 多继承
#include <iostream.h>
class base1{
public,
base1() { cout << "构造 base1" <<endl; }
~base1() { cout << "析放 base1" <<endl; }
};
class base2{
public,
base2() { cout << "构造 base2" <<endl; }
~base2() { cout << "析放 base2" <<endl; }
};
class base3{
public,
base3() { cout << "构造 base3" <<endl; }
~base3() { cout << "析放 base3" <<endl; }
};
39
续上 例 4.4.3
class base4{
public,
base4() { cout << "构造 base4" <<endl; }
~base4() { cout << "析放 base4" <<endl; }
};
class derived:public base2,public base1,public base3
{
private,
base4 member;
derived():base2(),base1(),base3() // 可缺省
{ }
~derived() { }
}
void main( )
{
derived d;
}
40
例 4.4.4 园 circle.cpp[例 4.15]
#include <iostream.h>
#include <graphics.h>
#include <conio.h>
class location{ //定义 类 Location
protected,
int x,y; // x,y 为保护成员
public,
location(int initx,int inity) //基类 Location构造函数
{ x=initx; y=inity; }
};
class point:public location{ //定义 类 point,为类 Location 的公有
派生类
public,
point(int initx,int inity):location(initx,inity) //point 类构造函数
{ }
void show() //用当前颜色画点
{ putpixel(x,y,getcolor()); }
void hide() //抹去显示点
{ putpixel(x,y,getbkcolor()); }//即用背景颜色画点
void moveto(int newx,int newy) //移动显示点
41
续上 _1 例 4.4.4 园 circle.cpp[例 4.15]
{ hide(); //抹去原有点
x=newx; y=newy; //设置新座标位置
show(); //画新点
}
};
class circles:public point{ //定义 类 circle,为类 point 的公有派生类
private,
int radius;
public,
circles(int initx,int inity,int initradius):point(initx,inity)
{ radius=initradius; } //circle 类构造函数
void show() //画一个园
{ circle(x,y,radius); }
void hide() //抹去已显示园
{ unsigned int tempcolor;
tempcolor=getcolor(); //记下当前颜色
setcolor(getbkcolor()); //将背景颜色设置为当前色
circle(x,y,radius); //用此颜色画园,相当于抹去园
42
续上 _2 例 4.4.4 园 circle.cpp[例 4.15]
setcolor(tempcolor); //将当前颜色恢复到原来状态
}
void expand(int expandby) //放大显示的园
{
hide(); //删去原有园
radius+=expandby; //增大园半径
if (radius<0) radius=0; //避免负值
show(); //画新园
}
void moveto(int newx,int newy) //移动显示的园
{ hide();
x=newx; y=newy;
show();
}
void contract(int contractby) //缩小显示的园
{ expand(-contractby); }
};
43
续上 _3 例 4.4.4 园 circle.cpp[例 4.15]
void main()
{
int gdriver=DETECT,gmode;
initgraph(&gdriver,&gmode,"d:\\borlandc\\bgi"); //初始化屏幕成
图形模式
circles mycircle(100,200,50); //定义一个园对象
setcolor(10); //设置当前色
mycircle.show( ); //画一个园
getch( );
for(int i=0; i<=100; i++) //慢慢移动园
{ for(long j=0; j<=1000000; j++);
mycircle.moveto(100+i,200); }
getch( );
44
int k=0;
for(i=0; i<=50; i++) //慢慢放大园
{ for(long j=0; j<=1000000; j++);
mycircle.expand(i-k);
k=i; }
getch( );
k=0;
for(i=0; i<=75; i++) //慢慢缩小园
{ for(long j=0; j<=1000000; j++);
mycircle.contract((i-k));
k=i; }
getch( );
closegraph( ); //关闭图形模式
}
45
例 4_16在圆内显示正文
例 4_17简单的大学管理系统
Location Location
Circle GMseeage
MCircle
Data_rec
teacher student
staff
虚 虚
46
例 4.4.5 单一继承实例
#include <iostream.h>
class BoxShape
{ public,
int width,height;
int SetWidth(int w) { width=w; }
int SetHeight(int h) { height=h; }
};
class ColorBoxShape:public BoxShape
{ public,
int color;
void SetColor(int c) { color=c; }
void print( )
{ cout << "颜色是,"<<color<<" 宽是,"<<width<<" 高
是,"<<height<<endl; }
};
ColorBoxShape cbox;
void main()
{ cbox.SetColor(5);
cbox.SetWidth(3);
cbox.SetHeight(50);
cbox.print( );
}
47
例 4.4.6 class Furniture //创建一个公共的父类
{ int color,width,height;
public,
Furniture(int,int,int); //Furniture 构造函数声明
int GetColor() { return color; }
int GetWidth() { return width; } int GetHeight() { return height; }
};
Furniture::Furniture(int c,int w,int h) //Furniture 构造函数
{ color=c; width=w; height=h; }
//以上是 Furniture类定义 class BookShelf:public Furniture //派生一个类 BookShelf
{ int shelves;
public,
BookShelf(int,int,int,int); //BookShelf 构造函数声明
int GetShelves() { return shelves; } void print()
{ cout<<"书架数据,"<<shelves<<endl;
cout<<"颜色,"<<GetColor()<<endl;
cout<<" 宽,"<<GetWidth()<<endl;
cout<<" 高,"<<GetHeight()<<endl; }
};
48
续上 例 4.4.6
BookShelf::BookShelf(int c,int w,int h,int s):Furniture(c,w,h)
{ shelves=s; }
//以上是 BookShelf类定义
class Table:public Furniture
{ int drawers,material;
public,
Table(int,int,int,int,int); //Table 构造函数声明
int GetDrawers() { return drawers; }
int GetMaterial() { return material; }
void print()
{ cout<<"书桌数据,"<<drawers<<" "<<material<<endl;
cout<<"颜色,"<<GetColor()<<endl;
cout<<" 宽,"<<GetWidth()<<endl;
cout<<" 高,"<<GetHeight()<<endl;
}
};
Table::Table(int w,int h,int c,int d,int m):Furniture(c,w,h)
{ drawers=d; material=m; }
//以上是 Table类定义
49
void main()
{ Table d(1,2,3,4,5);
d.print();
BookShelf b(10,11,12,13);
b.print();
}
例 4.4.7 派生类中访问基类成员的例子
class base
{ //.....,
public,void fun();
};
class derived1:private base
{ public,void funder1(){ 可以访问 fun() }
};
class derived2:public derived1
{ public,void funder2(); { 可以访问 funder1(),不能访问 fun() }
};
50
例 4.4.8 说明保护成员概念的例子
#include <iostream.h>
class base
{ protected,
int x,y; //若定义成私有成员 x,y
public,
void set(int a,int b) { x=a; y=b; }
void show() { cout<<x<<" "<<y<<endl; }
//int retx() { return x; }
//int rety() { return y; }
};
class derived:public base
{ private,
int k;
public,
void setk() { k=x*y; } //这里就不能直接用 x,y
// { k=retx()*rety(); }
void showk() { cout<<k<<endl; }
};
void main()
{ derived d;
d.set(5,8);
d.show();
d.setk();
d.showk();
}
51
例 4.4.9 虚基类的构造函数 //补 4_1.cpp
#include <iostream.h>
class base {
public,
base( ) { cout << "this is base class" <<endl; }
};
class base1 {
public,
base1( ) { cout << "this is base1 class" <<endl; }
};
class level1, public base1,virtual public base{
public,
level1( ) { cout << "this is level1 class" <<endl; }
};
class level2, public base1,virtual public base{
public,
level2( ) { cout << "this is level2 class" <<endl; }
};
52
class toplevel,virtual public level2,public
level1 {
public,
toplevel( ) { cout << "this is
toplevel class" <<endl; }
};
void main( )
{
toplevel top;
}
base1 base
level1 level2
toplevel
虚 虚
虚
53
4.5 赋值兼容规则
赋值兼容规则是指在公有派生情况下,派生类的对象可用到基类对
象的那些地方 。
1,派生类的对象可以赋给基类的对象
derived 类是 base 类的派生
dervied d;
base b;
b=d;
2,派生类的对象可以初始化基类的引用
derived d;
base& br=b;
3,派生类的对象的地址可以赋给指向基类的指针
derived d;
base *pb=&d;
54
例 单一继承的赋值兼容规则
class Location { //定义点类
private,
int X,Y;
public,
Location(int a,int b) //Location 构造函数
{ X=a; Y=b; }
int GetX() { return X; }
int GetY() { return Y; }
int distance(Location& p) //引用
{ int dx=X-p.X;
int dy=Y-p.Y;
return (int)sqrt(dx*dx+dy*dy);
}
};
class ellipse, public Location { // 定义椭园类
private,int L1,L2; //椭园的短轴和长轴
public,
ellipse(int i,int j,int k,int l):Location(i,j)
{ L1=k; L2=l; } //椭园类构造函数
};
55
续例 单一继承的赋值兼容规则
class circle, public ellipse {
private,
void GetL1() { }
void GetL2() { }
public,
circle(int x,int y,int r):ellipes(x,y,r,r)
{ } // 园类的构造函数 裁剪
};
void main( )
{ circle c(10,20,30);
ellipes e(50,70,150,300);
cout << "园和椭园的园心距离是,"<<c.distance(e)<<endl;
}
56
1
4.1 派生类的概念
4.1.1 为什么要使用继承
从现有类出发建立新的类,使新类继承老类的特点和功能,并
且又加上自己的特点和功能的程序设计方法,是面向对象程序设
计一个重要的技术。
老的类叫 基类,新的类叫 派生类 。
对派生类可做如下几种变化,
(1) 可以增加新的成员数据。
(2) 可以增加新的成员函数。
(3) 可以重新定义己有的成员函数。
(4) 可以改变现有成员的属性。
C++中有两种继承,
单一继承 和 多重继承 。
对于单一继承
派生类只能有一个基类;
对于多重继承
派生类可以有多个基类。
昆 虫
有 翅 类 无 翅 类
蛾 苍蝇 蝴蝶 单一继承
继
承 派生
窗 口 滚 动 条
可滚动的窗口
多重继承
2
4.1.1为什么要使用继承?
class person{
private,
char name[10];
int age;
char sex;
public,
void print();
};
比较二个类
class employee{
private,
char name[10];
int age;
char sex;
char
department[20];
float salary;
public,
void print();
};
3
4.1.2 派生类的声明
基类,
class 类名
{
类的内容
};
派生方式可为公有派生 (用 public)和私有派生 (用 private或缺省 )。
class person{
private,
char name[10];
int age;
char sex;
public,
void print();
};
声明派生类,
class 派生类名, 派生方式 基类名
{
//派生类新增的数据成员和函数成员
};
class employee, public person {
private,
char department[20];
float salary;
public,
void print();
};
4
1,私有派生
(1) 私有派生类对基类成员的访问
基类的公有成员相当于派生类的私有成员,派生类的成员数
可以访问它。但基类的私有成员派生类的成员函数不可以访问它。
例 4.1
#include <iostream.h>
class base{ //定义基类
int x;
public,
void setx(int n) { x=n; }
void showx() { cout << x << endl; }
};
class derived, private base { //定义私有派生类
int y;
public,
void setxy(int n,int m)
{ setx(n); y=m; }
void showxy()
{ cout << x << y << endl; } // 非法 不能访问 x,改为 showx()
};
void main()
{ derived obj;
obj.setxy(10,20);
obj.showxy();
}
5
(2) 外部函数 (main)对私有派生类继承来的成员的访问
私有派生时,基类的所有成员在派生类中都成为私有成员,外部函
数不能访问。
例 4.2
#include <iostream.h>
class base{ //定义基类
int x;
public,
void setx(int n) { x=n; }
void showx() { cout << x << endl; }
};
class derived, private base { //定义私有派生类
int y;
public,
void sety(int n) { y=n; }
void showy()
{ cout << y << endl; }
};
void main()
{ derived obj;
obj.setx(10); //出错
obj.sety(20); //合法
obj.showx(); //出错
obj.showy(); //合法
}
6
2,公有派生
在公有派生中,基类的私有成员对派生类来说仍是基类的私有
成员,只能通过基类的公有成员函数访问 (相当于可访问性保持
不变 ),不允许外部函数和派生类的成员函数直接访问。基类中
的公有成员相当于派生类的公有成员,外部函数和派生类的成员
函数可直接访问。
例 4.3
#include <iostream.h>
class base{ //定义基类
int x;
public,
void setx(int n) { x=n; }
void showx() { cout << x << endl; }
};
class derived, public base {//定义公有派生类
int y;
public,
void sety(int n) { y=n; }
void showy()
{ cout << y << endl; }
};
void main()
{ derived obj;
obj.setx(10); //合法
obj.sety(20); //合法
obj.showx(); //合法
obj.showy(); //合法
}
7
在作类派生时,可能导致几个类使用同一函数名或变量名,这
就会产生二义性。
如 class X {
public,int f();
};
class Y, public X {
public,int f();
int g();
};
这里 b.f() 用的是 Y 类的 f() 还是 X 类的 f()? 用的是 Y 类的
f(),编译器会自动从继承底向上搜索,这叫作支配规则。如果一
定要用 X 类的 f(),则可写成 b.X::f(); X::叫作用域分辨符。
#include <iostream.h>
void main( )
{ X a;
Y b;
int i=a.f();
int j=b.f();
cout << "i=" << i << endl;
cout << "j=" << j << endl;
}
8
表 4.1公有派生和私有派生的访问特性
基类成员 基类私有成员 基类公有成员 基类保护成员
派生方式 p r i v a t e p u b l i c p r i v a t e p u b l i c p r i v a t e p u b l i c
派生类成员
外部函数
不可访问
不可访问
不可访问
不可访问
可访问
不可访问
可访问
可访问
可访问
不可访问
可访问
不可访问
9
4.1.3 基类的保护成员作用
无论私有派生还是公有派生,派生类无权访问它的基类的私有
成员。派生类要想访问它的基类的私有成员,只能通过调用基类
的成员函数的方式实现,对这有时会带来不便。C++还提供一
种访问属性 protected 保护性成员。基类的保护成员具有私有成
员和公有成员双重角色:对自已的对象而言它是私有成员,即不
能被其他程序或函数访问 (只能被类中的成员函数访问 );而对派
生类而言,它又相当于是基类的公有成员。当公有派生时,基类
的保护成员在派生类也为保护成员。当私有派生时,相当于派生
类的私有成员,派生类的成员函数可以访问它。
10
void main ()
{ samp obj(20,30);
obj.a=88; //非法,类的私有成员不能对外
obj.b=99; //非法,类的保护成员相当于私
有成员不能对外
obj.c=50 //合法,类的公有成员能对外
cout << obj.geta() << ' ';
cout << obj.getb() << ' ' <<obj.c<<endl;
}
例 4.4 保护成员的例子
#include <iostream.h>
class samp{
int a;
protected,
int b;
public,
int c;
samp(int n,int m )
{ a=n; b=m; } //构造函数
int geta() { return a; }
int getb() { return b; }
};
11
例 4.5 保护成员被公有派生后的例子
#include <iostream.h>
class base {
protected,
int a,b; //保护成员
public,
void setab(int n,int m) { a=n; b=m; }
};
class derive:public base {
int c;
public,
void setc(int n) { c=n; }
void showabc()//a,b 相当于派生类的私有成员,成员函数可访问
{ cout <<a<<' '<<b<<' '<<c<<endl; }
};
void main()
{ derive obj;
obj.setab(2,4); //基类的公有成员相当于派生类的公有成员,可被外
界访问
obj.setc(3);
obj.showabc();
}
12
例 4.6 保护成员被私有派生后的例子
#include <iostream.h>
class base {
protected,
int a; //保护成员
public,
void seta(int sa) { a=sa; }
};
class derive2:public derive1 {
int c;
public,
void setc(int sc) { c=sc; }
void show()
{ cout<<"a="<<a<<endl; //非法,保护成员 a 被 derive1私有派生后
成为 derive1的私有成员,dervie2中不能 //直接访问
cout<<“b=”<<b<<endl; //合法
cout<<“c=”<<c<<endl; } //合法
};
void main()
{ base op1;
op1.seta(1);
derive1 op2;
op2.setb(2);
derive2 op3;
op3.setc(3);
op3.show();
}
13
class derive1:private base {
protected,
int b;
public,
void setb(int sb) { b=sb; }
};
表 4.2 基类成员在派生类中和外部函数中的访问权限
派 生 方 式 基类中的 访问权限 派生类中的 访问权限
public
( 公有派生 )
public
protected
private
public
protected
private
private
( 私有派生 )
public
protected
private
private
private
14
保护成员的这种性质还可传播给派生的派生。
例 保护成员的性质传播给派生的派生的例子。
class Base
{ int value;
protected,void SetValue(int v) { value=v; }
};
class First, public Base
{ int total;
public,void SetTotal(int t)
{ total=t;
SetValue(t); //使用基类的保护成员,相当于本类的保护成员
}
}; class Second, public First
{ int count;
public,
void SetCount(int c)
{ count=c; SetValue(c); //使用基类的保护成员,相当于本类的保护成员
}
};
15
4.2 派生类的构造函数和析构函数
当一个派生类有构造函数时,则它必须对自己的数据成员和
继承而来的基类数据成员初始化,这和前面 (第三章 )讲的类定义
中有对象作为数据成员类似。因此需要由派生类的构造函数调用
基类的构造函数来完成。
16
4.2.1 派生类的构造函数和析构函数执行顺序
当创建派生类对象时,首先执行基类的构造函数,随后再执行
派生类的构造函数;当撤消派生类对象时,则先执行派生类的析
构函数,随后再执行基类的析构函数。
例 4.7
#include <iostream.h>
class base {
public,
base( ) { cout << "执行 base 类的构造函数 \n"; }
~base( ) { cout <<"执行 base 类的析构函数 \n"; }
};
class derive:public base {
public,
dervie( ):base( ) { cout << "执行 dervie 类的构造函数 \n"; }
~dervie( ) { cout <<"执行 dervie 类的析构函数 \n"; }
};
void main( )
{ dervie op;
}
17
4.2.2 派生类的构造函数和析构函数的定义规则
当派生类的基类构造函数含有参数时,必须定义派生类的构造函数。
格式为,
派生类的构造函数名 (参数表 ):基类的构造函数名 (参数表 )
{ //.....,} 其中,基类构造函数参数,来源于派生类的构造函
数的参数表。
例 4.8 #include <iostream.h> class base {
int i; public,
base(int n) //基类的构造函数 { cout << "执行 base 基类的构造函数 \n";
i=n; }
~base( ) { cout <<"执行 base 基类的析构函数 \n"; } void showi( ) { cout << i << endl; }
}; class derive:public base {
int j; public,
dervie(int n,int m):base(m) //派生类的构造函数 { cout << "执行 dervie 派生类的构造函数 \n";
j=n; }
~dervie( ) { cout <<"执行 dervie 派生类的析构函数 \n"; } void showj( ) { cout << j << endl; }
};
void main( )
{ derive
obj(30,40);
obj.showi( );
obj.showj( );
}
18
当派生类中还含有对象成员时,其构造函数的格式为,
派生类的构造函数名 (参数表 ):基类的构造函数名 (参数表 ),对象成员
1(参数表 ),...,
对象成员 n(参数表 ) { //.....,}
定义派生类对象时构造函数执行顺序,
基类的构造函数 对象成员构造函数 派生类的构造函数;
当撤消派生类对象时,析构函数执行顺序正好相反。 例 4.9#include <iostream.h>
class base { int x;
public,base(int i) //基类的构造函数
{ cout << "执行 base 基类的构造函数 \n"; x=i;
} ~base() { cout <<"执行 base 基类的析构函数 \n"; }
void show() { cout << "x=" << x << endl; } };
class derive:public base { base d;
public, dervie(int i):base(i),d(i) //派生类的构造函数
{ cout << "执行 dervie 派生类的构造函数 \n"; } ~dervie() { cout <<"执行 dervie 派生类的析构函数 \n"; }
};
void main()
{ derive
obj(5);
obj.show();
}
说明 P-105
19
4.3 多重继承
从多个类派生出一个类,也即一个类从多个基类继承称为多继承。
4.3.1 多重继承的声明
多重继承的声明格式如下,
class 派生类名, 派生方式 1 基类名 1,...,派生方式 n 基类名 n
{
//派生类新增的数据成员和函数成员
};
派生类继承了基类 1 到 基类 n的所有数据成员和函数成员,其访问
权限规则与单继承情况一样,多继承可看成单一继承的扩展。
例 4.10
#include <iostream.h>
class Y {
int b;
public,
void setY(int x) { b=x; }
void showY()
{ cout <<"b="<<b<<endl; }
};
class X {
int a;
public,
void setX(int x) { a=x; }
void showX()
{ cout <<"a="<<a<<endl; }
};
20
class Z,public X,private Y {
int c;
public,
void setZ(int x,int y) { c=x; setY(y); }
void showZ() { showY(); cout<<"c="<<c<<endl; }
// void setZ(int x,int y,int z) { a=x; setY(y); c=z; }
// void showZabc() { cout<<"a="<<a<<endl;
// showY(); cout<<"c="<<c<<endl; }
};
void main()
{ Z obj;
obj.setX(3);
obj.showX();
obj.setY(4); //错误
obj.showY(); //错误
obj.setZ(6,8);
obj.showZ();
// obj.setZ(1,2,3);
// obj.showZabc();
}
21
再讲一个例
class A {
private,
int a;
public,
void setA(int);
void showA( );
};
class B {
private,
int b;
public,
void setB(int);
void showB( );
};
A类的公有成员 (和保护成员 ) C类中仍是公有的 (保护的 )
B类所有成员 C类中是私有的。
class C:public A,private B {
private,
int c;
public,
void setC(int,int);
void showC( );
};
22
完整的程序如下,
#include <iostream.h>
class A {
private,
int a;
public,
void setA(int x){ a=x; }
void showA(){ cout << a <<endl; }
};
class B {
private,
int b;
public,
void setB(int x){ b=x; }
void showB(){ cout << b <<endl; }
};
23
class C:public A,private B {
private,
int c;
public,
void setC(int x,int y)
{ c=x; setB(y); }
void showC( )
{ showB( ); cout<< c <<endl; }
};
void main()
{ C obj;
obj.setA(3);
obj.showA();
obj.setB(5); //错误
obj.showB(); //错误
obj.setC(5,8);
obj.showC();
}
24
真正生产二义性的情况是,一个派生类从多个基类派生,而这些基
类又有一个相同的 基类,则在派生类访问这个共同基类的成员时
会产生二义性。
class A {
public,
void fun() ;
};
class B {
public,
void fun() ;
void gun() ;
};
class C,public A,public B {
void gun() ;
void hun() ;
}
若有对象 C obj ; obj.fun() 是使用 A 类的 fun() 呢? 也是
B 类的 fun()? 编译器无法自动确定,因为对 C 类来说 A 类和 B
类是同一级,我们可以用成员名限定 (作用域分辨操作符 )来人为
的消去二义性。
obj.A::fun(); obj.B::fun();
25
4.3.2 多重继承的构造函数和析构函数
设类 Y从 X1,X2,....Xn类派生 (当只有一个 X1时为单继承,否则
为多继承 ),则派生类 Y的构造函数 Y的一般形式为,
Y::Y(参数表 0):X1(参数表 1),X2(参数表 2),.....,Xn(参数表 n)
{ //… }
其调用顺序为先 X1,X2,...,Xn(从左向右 ),最后为 Y
析构函数的执行顺序和构造函数正好向反。
例 4.11
#include <iostream.h>
class X {
int a;
public,
X(int sa) { a=sa; } //X类构造函数
int getX() { return a; }
};
class Y {
int b;
public,
Y(int sb) { b=sb; } //Y类构造函数
int getY() { return b; }
};
26
class Z:public X,private Y {
int c;
public,
Z(int sa,int sb,int sc):X(sa),Y(sb)
{ c=sc; } //Z类构造函数
int getZ( ) { return c; }
int getY( ) { return Y::getY( ); }
};
void main()
{ Z obj(2,4,6);
int ma=obj.getX(); cout << "a=" << ma << endl;
int mb=obj.getY(); cout << "b=" << mb << endl;
int mc=obj.getZ(); cout << "c=" << mc << endl;
}
27
4.3.3 虚基类
1,为什么要引入虚基类。
一个派生类从多个基类派生,而这些基类又有一个相同的基类,
则
在派生类访问这个共同基类的成员时会产生二义性。虽然我们可以
用“作用域分辨操作符”区别,但这时基类的成员有二个副本 (拷
贝 )。
如,
class base {
public,
int b;
};
class base1:public base { };
class base2:public base { };
class derived,public base1,public base2 {
public,
int fun() { }
};
程序运行结果是
path base1==>8
path base2==>15
void main( )
{ derived d;
d.base1::b=8;
d.base2::b=15;
cout << "path
base1==>"<<d.base1::b<<endl;
cout << "path
base2==>”<<d.base2::b<<endl;
}
28
base base
base1 base2
derived
2,虚基类的概念
如果希望基类只有一个拷贝,则在定义时把基类说明成虚基类,
加上 virtual,
class base {
public,
int b;
};
class base1:virtual public base { };
class base2:virtual public base { };
class derived,public base1,public base2 {
public,
int fun() { }
};
这时程序运行结果是
path base1==>15
path base2==>15
这时也没有了二义性,可以写成 int i=d.b;
void main( )
{ derived d;
d.base1::b=8;
d.base2::b=15;
cout << "path
base1==>”<<d.base1::b<<endl;
cout << "path
base2==>”<<d.base2::b<<endl;
}
29
例 4.12
#include <iostream.h>
class base{
protected,
int a;
public,
base ( ) { a=5; }
};
class base1:public base {
public,
base1( ) { cout <<"base1 a="<<a<<endl; }
};
class base2:public base {
public,
base2( ) { cout <<"base2 a="<<a<<endl; }
};
class deeived, public base1,public base2 {
public,
derived( ) { cout <<"derived a="<<a<<endl; }
};
void main( )
{
derived obj;
}
30
例 4.13
#include <iostream.h>
class base{
protected,
int a;
public,
base() { a=5; }
};
class base1:virtual public base {
public,
base1() { cout <<"base1 a="<<a<<endl; }
};
class base2:virtual public base {
public,
base2() { cout <<"base2 a="<<a<<endl; }
};
class deeived, public base1,public base2 {
public,
derived() { cout <<"derived a="<<a<<endl; }
};
void main()
{
derived obj;
}
31
3,虚基类的初始化
调用各类构造函数的顺序原则如下,
(1) 同一层次中先调用虚基类的构造函数 (不论它在何处 ),再是非
虚基类。
(2) 若有多个虚基类在同一层次,则按它们声明的次序。
(3) 若虚基类前又由非虚基类派生而来,则仍先非虚基类,再这
虚基类。
class A {,..,};
class B {,..,};
class C,virtual public A,virtual public B {....};
class D,virtual public B,virtual public A {....};
class E,public C,public D {,.....,};
则建立 E 的对象时,
先 C 的 A 构造函数,C 的 B, C
再 D ( D 的 A,B是虚只一个副本 C 中已做 )
最后是 E
32
例 4.14
#include <iostream.h>
class base {
int a;
public,
base(int sa) //base类构造函数
{ a=sa; cout<<"构造 base 类 "<<endl; }
};
class base1:virtual public base {
int b;
public,
base1(int sa,int sb):base(sa) //base1类构造函数
{ b=sb; cout<<"构造 base1 类 "<<endl; }
};
class base2:virtual public base {
int c;
public,
base2(int sa,int sc):base(sa) //base2类构造函数
{ c=sc; cout<<"构造 base2 类 "<<endl; }
};
33
续上 例 4.14
class derived:public base1,public base2{
int d;
public,
derived(int sa,int sb,int sc,int sd),
base(sa),base1(sa,sb),base2(sa,ac) //derived类构造函数
{ d=sd; cout<<"构造 derived 类 "<<endl; }
};
void main()
{
derived obj(2,4,6,8);
}
34
4.4 应用举例
例 4.4.1 单继承
#include <iostream.h>
class base {
private,
int a;
public,
base(int i)
{ a=i; cout << "构造 base" << endl; }
~base()
{ cout << "析放 base" <<endl; }
};
class derived:public base {
private,
int d;
public,
derived(int i,int j):base(i) //derived 构造函数
{ d=j; cout << "构造 derived" << endl; }
~derived() //derived 析构函数
{ cout << "析放 derived" <<endl; }
};
35
续上 例 4.4.1
/* class derived:public base {
private,
int d;
base member; //对象作为数据成员
public,
derived(int i,int j,int k):base(i),member(j) //derived 构造函数
{ d=k; cout << "构造 derived" << endl; }
~derived() //derived 析构函数
{ cout << "析放 derived" <<endl; }
};
*/
void main( )
{
derived d(5,8);
}
36
例 4.4.2 多层单继承
#include <iostream.h>
class base1 {
private,
int a;
public,
base1(int i)
{ a=i; cout << "构造 base1" << endl; }
~base1()
{ cout << "析放 base1" <<endl; }
};
class base2:public base1 {
private,
int b;
public,
base2(int i,int j):base1(j)
{ b=i; cout << "构造 base2" << endl; }
~base2()
{ cout << "析放 base2" <<endl; }
};
37
续上 例 4.4.2
class derived:public base2 {
private,
int d;
public,
derived(int i,int j,int k):base2(i,j) //derived 构造函数
{ d=k; cout << "构造 derived" << endl; }
~derived() //derived 析构函数
{ cout << "析放 derived" <<endl; }
};
void main( )
{
derived d(5,8,9);
}
38
例 4.4.3 多继承
#include <iostream.h>
class base1{
public,
base1() { cout << "构造 base1" <<endl; }
~base1() { cout << "析放 base1" <<endl; }
};
class base2{
public,
base2() { cout << "构造 base2" <<endl; }
~base2() { cout << "析放 base2" <<endl; }
};
class base3{
public,
base3() { cout << "构造 base3" <<endl; }
~base3() { cout << "析放 base3" <<endl; }
};
39
续上 例 4.4.3
class base4{
public,
base4() { cout << "构造 base4" <<endl; }
~base4() { cout << "析放 base4" <<endl; }
};
class derived:public base2,public base1,public base3
{
private,
base4 member;
derived():base2(),base1(),base3() // 可缺省
{ }
~derived() { }
}
void main( )
{
derived d;
}
40
例 4.4.4 园 circle.cpp[例 4.15]
#include <iostream.h>
#include <graphics.h>
#include <conio.h>
class location{ //定义 类 Location
protected,
int x,y; // x,y 为保护成员
public,
location(int initx,int inity) //基类 Location构造函数
{ x=initx; y=inity; }
};
class point:public location{ //定义 类 point,为类 Location 的公有
派生类
public,
point(int initx,int inity):location(initx,inity) //point 类构造函数
{ }
void show() //用当前颜色画点
{ putpixel(x,y,getcolor()); }
void hide() //抹去显示点
{ putpixel(x,y,getbkcolor()); }//即用背景颜色画点
void moveto(int newx,int newy) //移动显示点
41
续上 _1 例 4.4.4 园 circle.cpp[例 4.15]
{ hide(); //抹去原有点
x=newx; y=newy; //设置新座标位置
show(); //画新点
}
};
class circles:public point{ //定义 类 circle,为类 point 的公有派生类
private,
int radius;
public,
circles(int initx,int inity,int initradius):point(initx,inity)
{ radius=initradius; } //circle 类构造函数
void show() //画一个园
{ circle(x,y,radius); }
void hide() //抹去已显示园
{ unsigned int tempcolor;
tempcolor=getcolor(); //记下当前颜色
setcolor(getbkcolor()); //将背景颜色设置为当前色
circle(x,y,radius); //用此颜色画园,相当于抹去园
42
续上 _2 例 4.4.4 园 circle.cpp[例 4.15]
setcolor(tempcolor); //将当前颜色恢复到原来状态
}
void expand(int expandby) //放大显示的园
{
hide(); //删去原有园
radius+=expandby; //增大园半径
if (radius<0) radius=0; //避免负值
show(); //画新园
}
void moveto(int newx,int newy) //移动显示的园
{ hide();
x=newx; y=newy;
show();
}
void contract(int contractby) //缩小显示的园
{ expand(-contractby); }
};
43
续上 _3 例 4.4.4 园 circle.cpp[例 4.15]
void main()
{
int gdriver=DETECT,gmode;
initgraph(&gdriver,&gmode,"d:\\borlandc\\bgi"); //初始化屏幕成
图形模式
circles mycircle(100,200,50); //定义一个园对象
setcolor(10); //设置当前色
mycircle.show( ); //画一个园
getch( );
for(int i=0; i<=100; i++) //慢慢移动园
{ for(long j=0; j<=1000000; j++);
mycircle.moveto(100+i,200); }
getch( );
44
int k=0;
for(i=0; i<=50; i++) //慢慢放大园
{ for(long j=0; j<=1000000; j++);
mycircle.expand(i-k);
k=i; }
getch( );
k=0;
for(i=0; i<=75; i++) //慢慢缩小园
{ for(long j=0; j<=1000000; j++);
mycircle.contract((i-k));
k=i; }
getch( );
closegraph( ); //关闭图形模式
}
45
例 4_16在圆内显示正文
例 4_17简单的大学管理系统
Location Location
Circle GMseeage
MCircle
Data_rec
teacher student
staff
虚 虚
46
例 4.4.5 单一继承实例
#include <iostream.h>
class BoxShape
{ public,
int width,height;
int SetWidth(int w) { width=w; }
int SetHeight(int h) { height=h; }
};
class ColorBoxShape:public BoxShape
{ public,
int color;
void SetColor(int c) { color=c; }
void print( )
{ cout << "颜色是,"<<color<<" 宽是,"<<width<<" 高
是,"<<height<<endl; }
};
ColorBoxShape cbox;
void main()
{ cbox.SetColor(5);
cbox.SetWidth(3);
cbox.SetHeight(50);
cbox.print( );
}
47
例 4.4.6 class Furniture //创建一个公共的父类
{ int color,width,height;
public,
Furniture(int,int,int); //Furniture 构造函数声明
int GetColor() { return color; }
int GetWidth() { return width; } int GetHeight() { return height; }
};
Furniture::Furniture(int c,int w,int h) //Furniture 构造函数
{ color=c; width=w; height=h; }
//以上是 Furniture类定义 class BookShelf:public Furniture //派生一个类 BookShelf
{ int shelves;
public,
BookShelf(int,int,int,int); //BookShelf 构造函数声明
int GetShelves() { return shelves; } void print()
{ cout<<"书架数据,"<<shelves<<endl;
cout<<"颜色,"<<GetColor()<<endl;
cout<<" 宽,"<<GetWidth()<<endl;
cout<<" 高,"<<GetHeight()<<endl; }
};
48
续上 例 4.4.6
BookShelf::BookShelf(int c,int w,int h,int s):Furniture(c,w,h)
{ shelves=s; }
//以上是 BookShelf类定义
class Table:public Furniture
{ int drawers,material;
public,
Table(int,int,int,int,int); //Table 构造函数声明
int GetDrawers() { return drawers; }
int GetMaterial() { return material; }
void print()
{ cout<<"书桌数据,"<<drawers<<" "<<material<<endl;
cout<<"颜色,"<<GetColor()<<endl;
cout<<" 宽,"<<GetWidth()<<endl;
cout<<" 高,"<<GetHeight()<<endl;
}
};
Table::Table(int w,int h,int c,int d,int m):Furniture(c,w,h)
{ drawers=d; material=m; }
//以上是 Table类定义
49
void main()
{ Table d(1,2,3,4,5);
d.print();
BookShelf b(10,11,12,13);
b.print();
}
例 4.4.7 派生类中访问基类成员的例子
class base
{ //.....,
public,void fun();
};
class derived1:private base
{ public,void funder1(){ 可以访问 fun() }
};
class derived2:public derived1
{ public,void funder2(); { 可以访问 funder1(),不能访问 fun() }
};
50
例 4.4.8 说明保护成员概念的例子
#include <iostream.h>
class base
{ protected,
int x,y; //若定义成私有成员 x,y
public,
void set(int a,int b) { x=a; y=b; }
void show() { cout<<x<<" "<<y<<endl; }
//int retx() { return x; }
//int rety() { return y; }
};
class derived:public base
{ private,
int k;
public,
void setk() { k=x*y; } //这里就不能直接用 x,y
// { k=retx()*rety(); }
void showk() { cout<<k<<endl; }
};
void main()
{ derived d;
d.set(5,8);
d.show();
d.setk();
d.showk();
}
51
例 4.4.9 虚基类的构造函数 //补 4_1.cpp
#include <iostream.h>
class base {
public,
base( ) { cout << "this is base class" <<endl; }
};
class base1 {
public,
base1( ) { cout << "this is base1 class" <<endl; }
};
class level1, public base1,virtual public base{
public,
level1( ) { cout << "this is level1 class" <<endl; }
};
class level2, public base1,virtual public base{
public,
level2( ) { cout << "this is level2 class" <<endl; }
};
52
class toplevel,virtual public level2,public
level1 {
public,
toplevel( ) { cout << "this is
toplevel class" <<endl; }
};
void main( )
{
toplevel top;
}
base1 base
level1 level2
toplevel
虚 虚
虚
53
4.5 赋值兼容规则
赋值兼容规则是指在公有派生情况下,派生类的对象可用到基类对
象的那些地方 。
1,派生类的对象可以赋给基类的对象
derived 类是 base 类的派生
dervied d;
base b;
b=d;
2,派生类的对象可以初始化基类的引用
derived d;
base& br=b;
3,派生类的对象的地址可以赋给指向基类的指针
derived d;
base *pb=&d;
54
例 单一继承的赋值兼容规则
class Location { //定义点类
private,
int X,Y;
public,
Location(int a,int b) //Location 构造函数
{ X=a; Y=b; }
int GetX() { return X; }
int GetY() { return Y; }
int distance(Location& p) //引用
{ int dx=X-p.X;
int dy=Y-p.Y;
return (int)sqrt(dx*dx+dy*dy);
}
};
class ellipse, public Location { // 定义椭园类
private,int L1,L2; //椭园的短轴和长轴
public,
ellipse(int i,int j,int k,int l):Location(i,j)
{ L1=k; L2=l; } //椭园类构造函数
};
55
续例 单一继承的赋值兼容规则
class circle, public ellipse {
private,
void GetL1() { }
void GetL2() { }
public,
circle(int x,int y,int r):ellipes(x,y,r,r)
{ } // 园类的构造函数 裁剪
};
void main( )
{ circle c(10,20,30);
ellipes e(50,70,150,300);
cout << "园和椭园的园心距离是,"<<c.distance(e)<<endl;
}
56