第七章继承机制第七章 继承机制
§ 7.1 继承的基本概念
§ 7.2 C++语言的继承机制
§ 7.3 继承与构造函数、析构函数
§ 7.4 多重继承
§ 7.5 重复继承
§ 7.1 继承的基本概念继承的含义
不同的类具有相似的特征
-普通电话,手机,IC卡电话
-麻雀,燕子,鸽子,大雁
分类鸟麻雀 大雁鸽子燕子
§ 7.1 继承的基本概念
由简单到复杂汽车小汽车 卡车 旅行车工具车 小轿车 出租车
§ 7.1 继承的基本概念概念
基类( base class)
派生类( derived class)
一个类可以既是基类,又是派生类
(基类与派生类是相对而言的)
§ 7.1 继承的基本概念派生类与基类
特殊类与一般类的关系 (个性与共性的关系)
特殊类 (子类、派生类) 的对象拥有一般类 (父类、基类)
对象的全部属性与服务,称为特殊类对一般类的继承
继承是描述类之间共同特性的机制
继承是类的一种组织方式
§ 7.1 继承的基本概念继承的分类
单继承和多继承电话固定电话 移动电话 助教博士学生 教师单继承 多继承
§ 7.1 继承的基本概念继承的作用
作为类的构造机制扩充,形成现有类的特例 ——派生类组合,抽取出若干现有类的共性形成新的抽象层次 ——基类对现有类
软件复用使用继承可以方便的复用别人写的代码,从而提高程序的可靠性,提高开发效率
§ 7.2 C++语言的继承机制
继承的定义
class DERIVED:继承方式 BASE
{
public:
公有成员说明列表
protected:
受保护成员说明列表
private:
私有成员说明列表
};
派生类 基类
§ 7.2 C++语言的继承机制
例,class BASE
{
public:
void setA(int al);
private:
int a;
};
class DERIVED:public BASE
{
public:
void setB(int bl);
private:
int b;
};
注:默认的继承方式为,private,常用的继承方式是,public
基类(父类)
派生类(子类)
继承方式
§ 7.2 C++语言的继承机制
protected的作用
封闭性和开放性的结合
- 不能被一般函数访问
- 只能被派生类的成员函数访问
为派生类提供了访问基类成员的特权
§ 7.2 C++语言的继承机制派生类
派生类也是类
-可以有自己的数据成员和成员函数
派生类又是特殊的类
-基类的所有成员也是派生类的成员
-即:派生类继承了基类所有的成员
§ 7.2 C++语言的继承机制
三种继承方式
1、公有继承 (public)
基类的公有成员和保护成员作为派生类的成员时,它们都保持原有状态,而基类的私有成员仍然是私有的
2、私有继承 (private)
基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问
3、保护继承 (protected)
基类的公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数访问,基类的私有成员仍然是私有的
§ 7.2 C++语言的继承机制继承的方式
public,protected和 private
基类成员的访问控制 public继承 protected继承 private继承
public public protected private
protected protected protected private
private 不可访问 不可访问 不可访问基类成员在派生类中的访问控制
§ 7.2 C++语言的继承机制派生类成员的访问控制
派生类本身的成员
-遵循通用的访问控制策略
派生类继承来的成员(公有继承)
-派生类本身的成员函数可以访问基类的公有和保护成员
-派生类的对象只可以访问派生类和基类的公有成员
§ 7.2 C++语言的继承机制
分析公有继承方式
1、基类成员对其对象的可见性:
公有成员可见,其他不可见,保护成员同于私有成员
2、基类成员对派生类的可见性:
公有成员和保护成员可见,私有成员不可见,保护成员同于公有成员
3、基类成员对派生类对象的可见性:
公有成员可见,其他成员不可见结论:派生类的对象可以访问基类中的公有成员;
派生类的成员函数可以访问基类中的公有成员和保护成员
例 class BASE
{
public:
void setA(int al) {a=al;}
void setB(int bl) {b=bl;}
protected:
int b;
private:
int a;
};
class DERIVED:public BASE
{
public:
void setC(int cl) {c=cl;}
void setD(int dl) {d=dl;}
void set_A(int al) {a=al;} //
void set_B(int bl) {b=bl;} //
protected:
int d;
private:
int c;
};
§ 7.2 C++语言的继承机制
BASE obj1;
DERIVED obj2;
//派生类对象可以访问基类的公有成员
obj2.setA(1);
obj2.setB(2);
//可以直接访问派生类的公有成员
obj2.setC(3);
obj2.setD(4);
例:定义 BOX类描述立方体,定义 COLORBOX类描述彩色立方体
class BOX
{
public:
void setwidth(int w) {width=w;}
void setheight(int h) {height=h;}
int getwidth( ) {return width;}
int getheight( ) {return height;}
private:
int width,height;
};
class COLORBOX:public BOX
{
public:
void setcolor(int c) {color=c;}
int getcolor( ) {return color;}
private:
int color;
};
void main( )
{
COLORBOX cbox;
cbox.setcolor(3);
cbox.setwidth(150);
cbox.setheight(100);
cout<<“Cbox:”<<endl;
cout<<“Color:”<<cbox.getcolor( )
<<endl;
cout<<“Width:”<<cbox.getwidth( )
<<endl;
cout<<“Height:”<<cbox.getheight( )
<<endl;
}
§ 7.2 C++语言的继承机制访问控制小结
( public继承方式)
基类成员的访问控制对派生类成员的可访问性对派生类对象的可访问性在派生类中的访问控制
public 可访问 可访问 public
protected 可访问 不可访问 protected
private 不可访问 不可访问 private
§ 7.3 继承与构造函数、析构函数派生类对象的构造
派生类的构造函数中必须启动基类的构造函数(有缺省的构造函数除外)
基对象附加部分派生对象派生类成员 =基类成员 +附加成员
§ 7.3 继承与构造函数、析构函数构造函数、析构函数的执行顺序
构造派生类对象时先执行基类的构造函数再执行派生类的构造函数如果派生类中包含有子对象时,还要调用子对象的构造函数,其执行顺序为:
①祖先(先调用基类构造函数)
②客人(再调用子对象构造函数,如果有子对象)
③自己(最后调用派生类构造函数)
派生类对象撤销时析构函数的执行顺序与构造函数刚好相反
§ 7.3 继承与构造函数、析构函数
派生类构造函数的一般形式:
<派生类名 >(总参数表),<基类名 >(参数表 1),
<子对象名 >(参数表 2)
{
<派生类中数据成员初始化 >
}
例,class A
{
public:
A(int i,int j){a1=i; a2=j; cout<<“A?s constructor called.\n”;}
~A( ) {cout<<“A?s destructor called.\n”;}
void print( ) {cout<<“a1=“<<a1 <<endl <<“a2=“<<a2<<endl;}
private:
int a1,a2;
};
class B:public A
{
public:
B(int i,int j,int k,int l,int m);
~B( ) {cout<<“B?s destructor called.\n”;}
void print( ) {A::print(); cout<<“b1=“<<b1<<endl;}
private:
int b1;
A aa;
};
B::B(int i,int j,int k,int l,int m):A(i,j),aa(k,l)
{
b1=m;
cout<<“B?s constructor called.\n”;
}
void main()
{
B bb(1,2,3,4,5);
bb.print();
}
§ 7.3 继承与构造函数、析构函数单继承举例
磁盘 DISK
name
sides
tracks
sectors
byte per sector
FLOPPY DISK
write protect
HARD DISK
controller type
// DISK类的定义
class DISK
{
public:
DISK(char *nme,int side,int track,int spt,int bps);
long capacity( );
private:
char name[64];
int sides,int tracks,int sector_per_track,int byte_per_sector;
};
// DISK类的实现
DISK:,DISK (char *nme,int side,int track,int spt,int bps)
{
name=nme;
sides=side;
tracks=track;
sector_per_track=spt;
byte_per_sector=bps;
}
//FLOPPYDISK类的定义
class FLOPPYDISK:public DISK
{
public:
FLOPPYDISK(char *name,int side,int track,int spt,
int bps,bool up);
void setwriteprotect(int state);
private:
bool write_protect;
};
//FLOPPYDISK类的实现
FLOPPYDISK:,FLOPPYDISK(char *name,int sides,int
tracks,int spt,int bps,bool up):DISK(name,
sides,tracks,spt,bps)
{
write_protect=up;
}
//HARDDISK类的定义
class HARDDISK:public DISK
{
public:
HARDDISK (char *name,int sides,int tracks,int
spt,int bps,char *type);
void set_controller_type(char*);
private:
char controller_type[64];
};
//HARDDISK类的实现
HARDDISK:,HARDDISK (char *name,int sides,int tracks,
int spt,int bps,char *type):DISK(name,sides,tracks,spt,bps)
{
strcpy(controller_type,type);
}
例,class A
{
public:
A( ) {a=0; cout<<“A?s default constructor called.\n”;}
A(int i) {a=i; cout<<“A?s constructor called.\n”;}
~A( ) {cout<<“A?s destructor called.\n”;}
void print( ) {cout<<a<<“,”;}
int geta( ) {return a;}
private:
int a;
};
class B:public A
{
public:
B( ) {b=0; cout<<“B?s default constructor called.\n”;}
B (int i,int j,int k) ;
~B( ) {cout<<“B?s destructor called.\n”;}
void print( ) ;
private:
int b;
A aa;
};
B::B(int i,int j,int k):A (i),aa(j)
{
b=k;
cout<<“B?s constructor called.\n”;
}
void B::print( )
{
A::print( );
cout <<b<<“,”<<aa.geta( )<<endl;
}
void main( )
{
B bb[2];
bb[0]=B(1,2,5);
bb[1]=B(3,4,7);
for(int i=0; i<2; i++)
bb[i].print( );
}
程序执行结果
A?s default constructor called.
A?s default constructor called.
B?s default constructor called.
A?s default constructor called.
A?s default constructor called.
B?s default constructor called.
A?s constructor called.
A?s constructor called.
B?s constructor called.
B?s destructor called.
A?s destructor called.
A?s destructor called.
A?s constructor called.
A?s constructor called.
B?s constructor called.
B?s destructor called.
A?s destructor called.
A?s destructor called.
1,5,2
3,7,4
B?s destructor called.
A?s destructor called.
A?s destructor called.
B?s destructor called.
A?s destructor called.
A?s destructor called.
§ 7.3 继承与构造函数、析构函数派生类构造函数使用中应注意的问题
⑴ 派生类构造函数的定义中可以省略对基类构造函数的调用,其条件是在基类中必须有缺省的构造函数或者根本没有定义构造函数
(基类中没有定义构造函数,派生类根本不必负责调用基类构造函数)
见下例说明例,派生类构造函数使用中应注意的问题 (演示)
class A
{
public:
A( ){a=0;}
A(int i){a=i;}
void print(){cout<<a<<?,?;}
private:
int a;
};
class B:public A
{
public:
B( ){b1=b2=0;}
B(int i){b1=i; b2=0;}
B(int i,int j,int k):A(i),b1(j),b2(k){ }
void print() {A::print(); cout<<b1<<?,?<<b2<<endl;}
private:
int b1,b2;
};
void main()
{
B d1;
B d2(5);
B d3(4,5,6);
d1.print();
d2.print();
d3.print();
}
§ 7.3 继承与构造函数、析构函数
⑵ 当基类的构造函数使用一个或多个参数时,
则派生类必须定义构造函数,提供将参数传递给基类构造函数途径
(某些情况下,派生类构造函数的函数体可能为空,
仅起到参数传递作用)
见下例说明例:派生类构造函数使用中应注意的问题
class A
{
public:
A(int i,int j){a1=i;a2=j;}
……
private:
int a1,a2;
};
class B:public A
{
public:
B(int i,int j,int k,int l,int m);
……
private:
int b1;
A bb;
};
B::B(int i,int j,int k,int l,int m):A(i,j),bb(k,l)
{
b1=m;
}
派生类 B的构造函数有 5个参数,其中前 2个传给基类,接着 2个给子对象 bb,最后 1个传给类 B
§ 7.3 继承与构造函数、析构函数类型兼容性
基类与派生类有一种,类型 —子类型,的关系
单继承:形成有“什么是一种什么”关系的对象的集合
( IS-A relationship)
例:
一辆轿车是一种汽车一个经理是一种雇员一个方阵是一种矩阵子类型 ———类型
§ 7.3 继承与构造函数、析构函数
类 B继承类 A
则称:类 B是类 A的一个子类型( B 是一种 A)
或称:类 B兼容类 A(是轿车一定是汽车,反之不成立)
允许类 B的对象赋值给类 A的对象例,A aa;
B bb;
aa=bb; //合法,但反之不成立
指向基类对象的指针可以指向派生类的对象例,A *pa;
B *pb;
pa=pb; //合法,但反之不成立例:类型兼容性举例,分析程序执行结果
#include<iostream.h>
class A
{
public:
A( ){a=0;}
A(int i){a=i;}
void print( ){cout<<a<<endl;}
int geta( ){return a;}
private:
int a;
};
class B:public A
{
public:
B( ){b=0;}
B(int i,int j):A(i),b(j){}
void print( ) {A::print( );cout<<b<<endl;}
private:
int b;
};
void fun(A& d)
{
cout<<d.geta( )*10<<endl;
}
void main( )
{
B bb(9,5);
A aa(6);
aa=bb;
aa.print( );
A *pa=new A(8);
B *pb=new B(1,2);
pa=pb;
pa->print( );
fun(bb);
}
§ 7.4 多重继承
一个派生类具有多个基类
(派生类与每个基类之间的关系仍可看作是一个单继承)
派生类基类 1 基类 2 基类 n….
…...
§ 7.4 多重继承
多继承的定义格式
class <派生类名 >,<继承方式 1><基类名 1>,
<继承方式 2><基类名 2>,……
{
派生类类体
};
其中:继承方式 1,继承方式 2,…
可以是三种方式之一,
public
protected
private
§ 7.4 多重继承例,class A
{
……
};
class B
{
……
};
class C:public A,public B
{
……
};
派生类 C公有继承了两个基类(类 A和类 B)
§ 7.4 多重继承多继承的构造函数
定义格式:
<派生类名 >(总参数表 ),<基类名 1> (参数表 1),
<基类名 2> (参数表 2),……
<子对象名 > (参数表 n+1),….
{
派生类构造函数体
}
其中:总参数表中包含了其后的各个分参数表
§ 7.4 多重继承
多继承举例填充类填充颜色填充图形矩形类矩形长矩形宽填充矩形类填充矩形
§ 7.4 多重继承
Filled类的定义
class FILLED
{
public:
FILLED(int fillcolor,int filltype);
void fillgraph(SCOPE scope);
protected:
int fillcolor;
int filltype;
};
§ 7.4 多重继承
Rectangle类的定义
class RECTANGLE
{
public:
RECTANGLE(POINT top_left,POINT bottom_right,
int line_color);
RECTANGLE( );
long area( ); //求矩形面积
void draw( ); //绘制矩形
protected:
POINT top_left,bottom_right;
int line_color;
};
§ 7.4 多重继承
FilledRectangle类的定义
class FILLEDRECTANGLE,public FILLED,public RECTANGLE
{
public:
FILLEDRECTANGLE( POINT top_left,POINT bottom_right,
int line_color);
void draw( ); //绘制填充矩形
private:
getscope(SCOPE& scope);
};
§ 7.4 多重继承
FilledRectangle类的实现
void FILLEDRECTANGLE,,draw( )
{
RECTANGLE::draw( ); //调用基类的函数
SCOPE scope;
getscope(scope); //取矩形的范围
fillgragh(scope); //调用基类 (Filled)的函数
}
例:分析程序的执行结果
#include<iostream.h>
class B1
{
public:
B1(int i)
{
b1=i;
cout<<“B1.”<<i<<endl;
}
void print( ) {cout<<b1<<endl;}
private:
int b1;
};
class B2
{
public:
B2(int i)
{
b2=i;
cout<<“B2.”<<i<<endl;
}
void print( ){cout<<b2<<endl;}
private:
int b2;
};
class B3
{
public:
B3(int i)
{
b3=i; cout<<“B3.”<<i<<endl;
}
void print( )
{
cout<<b3<<endl;
}
int getb3( )
{
return b3;
}
private:
int b3;
};
class A,public B2,public B1
{
public:
A(int i,int j,int k,int l):B1(i),B2(j),bb(k)
{
a=l; cout<<“A.”<<l<<endl;
}
void print( )
{
B1::print( );
B2::print( );
cout<<a<<?,?<<bb.getb3( )<<endl;
}
private:
int a;
B3 bb;
};
void main( )
{
A aa(1,2,3,4);
aa.print( );
}
程序说明:
⑴ 派生类 A的构造函数组成:
A(int i,int j,int k,int l):B1(i),B2(j),bb(k),a(l)
⑵ 派生类 A的构造函数执行顺序按派生类定义中的顺序,既先执行 B2的构造函数,再执行 B1的构造函数
⑶ 作用域运算符,:用于解决作用域冲突问题
B1和 B2中都有 print( )函数,假设派生类 A中没有 print( )函数,当执行 aa.print( )时,就会产生二义性
§ 7.4 多重继承
§ 7.4 多重继承
二义性问题在多继承情况下,对基类中某个成员的访问可能不唯一
(如:上例中的 print( )函数)
class A,public B2,public B1
{
public:
void print( )
{
B1::print( );
B2::print( );
}
};
void main( )
{
A aa(1,2,3,4);
aa.print( ); //假设派生类 A中没有定义 print函数,如何?
}
例,
class A
{
public:
void f( );
};
class B
{
public:
void f( );
void g( );
};
class C:public A,public B
{
public:
void g( );
void h( );
};
如果定义了一个类 C的对象即,C c1;
则对函数 f( )的访问
c1.f( );
具有二义性,是访问 A中的 f( ),还是访问 B中的 f( )?
解决方法:用作用域运算符,:
如,c1.A:,f( );
或,c1.B:,f( );
§ 7.4 多重继承解决二义性方法,用作用域运算符,:
如,c1.A:,f( ); 或,c1.B:,f( );
最好是在类 C中定义一个同名成员 f( ),类 C中的 f( )再根据需要来决定调用哪一个基类的 f( ),
因为 c1.f( )是调用 C::f( )
§ 7.4 多重继承
基类和派生类中同时出现的同名成员函数不存在二义性问题
基类 B中有一个成员函数 g(),派生类 C中也有 g()
如果:
C c1;
c1.g(); //不存在二义性调用的是 C::g();而不是调用 B::g();
§ 7.4 多重继承
在多继承中,为了清楚地表示各类之间的关系,常采用一种称为 DAG的图示表示法如上例中的类 A、类 B、类 C可表示如下:
A{f( )} B{f( ),g( )}
C{g( ),h( )}
§ 7.4 多重继承
多级继承
(一个类即可作基类,也可作派生类)
例如人 员工作人员管理人员有线电话 无线电话脉冲电话 音频电话电 话
§ 7.4 多重继承多级继承
派生类的派生类的访问控制基类的公有、保护成员到派生类还是公有、保护,基类的私有成员,派生类不可访问派生类的派生类仍遵循这种访问控制规则
派生类的派生类的构造函数派生类对象创建时,调用直接基类的构造函数,间接基类的构造函数是通过直接基类的成员初始化参数表
§ 7.4 多重继承
Person类的定义
class PERSON
{
public:
PERSON(char *name,int age);
char *get_name( );
int get_age( );
protected:
char name[64];
int age;
};
Worker类的定义
class WORKER:public PERSON
{
public:
WORKER(char *name,int age,float wage,int workID);
void change_wage(int new_wage);
protected:
float wage;
int workID;
};
Worker类的实现
WORKER::WORKER(char *name,int age,float wage,int workID),
PERSON(name,age)
{
WORKER::wage=wage;
WORKER::workID=workID;
}
成员 参数
Manager类的定义
class MANAGER:public WORDER
{
public:
MANAGER(char *name,int age,float wage,int workID,
int bonus_level,char *office);
void change_office(char *new);
protected:
int bonus_level; //红利等级
char office[64]; //办公室
};
Manager类的实现
MANAGER::MANAGER(char *name,int age,float wage,int workID,int
bonus_level,char *office):WORKER(name,age,wage,workID)
{
MANAGER:,bonus_level= bonus_level;
strcpy(MANAGER::office,office);
}
§ 7.5 重复继承
重复继承一个派生类多次继承同一个基类
C++不允许如下的重复继承一个派生类直接继承同一个基类两次 (如左图 )
一个基类即是直接基类又是间接基类 (如右图 )
BB
D
B
B
B1
D
§ 7.5 重复继承
C++允许的重复继承
B2B1
D
B
ostreamistream
iostream
ios
派生类访问间接基类时,可能会出现二义性例,class A
{
public:
int a;
};
class B1,public A
{
private:
int b1;
};
class B2,public A
{
private:
int b2;
};
class C,public B1,public B2
{
public:
int f( );
private:
int c;
};
用图示表示
A{a} A{a}
B1{b1} B2{b2}
C{f( ),c}
如果:
C c1;
c1.a;
c1.A::a; 二义性改为:
c1.B1::a;
c1.B2::a; 消除二义性
§ 7.5 重复继承例:
教师学生在职研究生人作为学生的 Department------Management
作为教师的 Department------Computer
Name和 Sex 成员称为共享继承
Department 成员称为复制继承
C++只支持一种重复继承,共享继承或复制继承
Name
Sex
Department
无论作为学生、教师都是相同的派生出两份数据例:分析下列程序的输出结果 (演示)
#include<iostream.h>
class A
{
public:
A(int i) {a=i; cout<<“con.A\n”;}
void print( ) {cout<<a<<endl;}
~A( ) {cout<<“des.A\n”;}
private:
int a;
};
class B1:public A
{
public:
B1(int i,int j):A(i) {b1=j; cout<<“con.B1\n”;}
void print( ) {A::print( ); cout<<b1<<endl;}
~B1( ) {cout<<“des.B1\n”;}
private:
int b1;
};
class B2,public A
{
public:
B2(int i,int j):A(i) {b2=j; cout<<“con.B2\n”;}
void print( ) {A::print( ); cout<<b2<<endl;}
~B2( ) {cout<<“des.B2\n”;}
private:
int b2;
};
class C,public B1,public B2
{
public:
C(int i,int j,int k,int l,int m):B1(i,j),B2(k,l)
{c=m; cout<<“con.C\n”;}
void print( )
{
B1::print( );
B2::print( );
cout<<c<<endl;
}
~C( ) {cout<<“des.C\n”;}
private:
int c;
};
void main( )
{
C c1(1,2,3,4,5);
c1.print( );
}
输出结果:
con.A
con.B1
con.A
con.B2
con.C
1
2
3
4
5
des.C
des.B2
des.A
des.B1
des.A
§ 7.5 重复继承
虚基类如果要想使类 A在派生类 C中只产生一个基对象,则须将类 A定义为虚基类
虚基类为多条继承路经提供了一个汇合点
引入虚基类的目的,解决二义性问题
虚基类说明格式 (只用在重复继承中 )
在定义派生类时,在继承方式前加 virtual
例,class A
{
public:
void f( );
};
class B,virtual public A
{
…...
};
class C,virtual public A
{
…...
};
class D,public B,public C
{
public:
int g( );
};
以下不存在二义性
D n;
n.f( ); //正确
void D::g( );
{
f( ); //正确
}
没有 virtual
A A
B C
D
D中有两个类 A的子对象有 virtual
A
B C
D
D中只有一个类 A
的子对象
§ 7.5 重复继承虚基类的构造函数与析构函数
只有用于建立对象的那个派生类的构造函数调用虚基类构造函数,以保证对虚基类的子对象只初始化一次,但在其它派生类构造函数的成员初始化列表中仍要列出虚基类构造函数的调用
虚基类构造函数的调用优先于普通基类构造函数的调用例,class A
{
public:
A(char *s) {cout<<s<<endl;}
};
class B,virtual public A
{
public:
B(char *s1,char *s2),A(s1)
{cout<<s2<<endl;}
};
class C,virtual public A
{
public:
C(char *s1,char *s3),A(s1)
{cout<<s3<<endl;}
};
不执行虚基类 A
的构造函数不执行虚基类 A
的构造函数
class D,public B,public C
{
public:
D(char *s1,char *s2,char *s3,char *s4),B(s1,s2),
C(s1,s3),A(s1)
{cout<<s4<<endl;}
};
void main( )
{
D *ptr=new D(“class A”,“class B”,“class C”,“class D”);
delete ptr;
}
说明:
在派生类 B,C,D的构造函数的成员初始化列表中均包含了对虚基类 A的构造函数,但类 B,C中虚基类 A构造函数不被执行,只有派生类 D执行一次只执行一次第七章 小 结
继承的含义
继承的分类
派生类成员的访问控制
构造函数与析构函数