继承与类的派生

55
继继继继继继继

Upload: walter-hebert

Post on 02-Jan-2016

30 views

Category:

Documents


2 download

DESCRIPTION

继承与类的派生. 多继承 多继承的概念 所谓 多继承 就是一个 新类 是从 多个基类 中 派生 而成 的。例如,在一个面向对象的图形用户界面中,为用 户界面提供的 窗口 、 滚动条 、 对话框 和各种 操作按钮 都是通过类对象来实现的。如果希望在 既有类 的基础 上定义一个 新类具有两个以上既有类的全部属性和操 作,就可以通过多继承的方法完成 。如,可以由 窗口 类 和 滚动条类 共同派生出一个 可滚动的窗口新类 。. 在有些情况下,可以使用 类的组合关系 ,即将一些 既有类对象定义为新类的成员来实现与多继承派生 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 继承与类的派生

继承与类的派生

Page 2: 继承与类的派生

多继承

多继承的概念 所谓多继承就是一个新类是从多个基类中派生而成

的。例如,在一个面向对象的图形用户界面中,为用

户界面提供的窗口、滚动条、对话框和各种操作按钮

都是通过类对象来实现的。如果希望在既有类的基础

上定义一个新类具有两个以上既有类的全部属性和操

作,就可以通过多继承的方法完成。如,可以由窗口

类和滚动条类共同派生出一个可滚动的窗口新类。

Page 3: 继承与类的派生

在有些情况下,可以使用类的组合关系,即将一些

既有类对象定义为新类的成员来实现与多继承派生

的新类相同的功能。例如,同样是定义可滚动的窗

口类,可以以窗口类为基类单继承派生,并将滚动

条类对象作为新类的新增成员。在新类中窗口类的

属性和行为被继承,而滚动条的属性和行为并没有

被继承,对滚动条对象成员的使用是通过新类的内

部消息实现的。

Page 4: 继承与类的派生

多继承的定义 多重继承派生类定义的一般形式:

class 派生类名 : 继承方式 基类名 1, … 继承方式 基类名 n

{ 派生类新增的数据成员和成员函数 };

注意,在每个基类名之前必须有继承方式,如果缺省继承方式,则表示从该基类私有派生。例如:class c : public a,b {…}; // c 对 a 公有派生,对 b 私有

派生

class c : a, public b {…}; // c 对 a 私有派生,对 b 公有派生

class c : public a, public b {… }; // c 对 a,b 均为公有派生

Page 5: 继承与类的派生

• 例 多重继承。#include<iostream>

#include<cstring>using namespace std;

class Point{ //二维直角坐标点类protected:

float x,y;public:

Point(float x,float y){ this->x=x; this->y=y; }void Setxy(float x,float y){ this->x=x; this->y=y; }void Getxy(float &x,float &y){ x=this->x; y=this->y; }void ShowP( ){ cout<<'('<<x<<','<<y<<')'; }

};

Page 6: 继承与类的派生

class Color{ //颜色类protected:

char c[10];public:

Color(char*c){ strcpy(this->c,c); }void Setc(char*c){ strcpy(this->c,c); }char* Getc(char *c){ strcpy(c,this->c);

return c;}

};

Page 7: 继承与类的派生

class Circle:public Point,protected Color{//彩色圆类protected:

float r;public:

Circle(float x,float y,float r,char*c):Point(x,y),Color(c){ this->r=r; }

void Setr(float r){ this->r=r; }float Getr( ){ return r; }

float Area( ){ return r*r*3.14159f; }void Show( ){ cout<<"圆心 :"; ShowP();

cout<<",半径:"<<r<<",颜色:"<<c<<",面积:"<<Area()<<'\n';}

};

intmain(void){ Circle c(1,2,3,"green");

c.Show();return 0;

}

• 派生类Circle的构造函数调用基类Point和基类Color的构造函数

程序运行结果:圆心:(1,2),半径:3,颜色:green,面积:28.2743

Page 8: 继承与类的派生

初始化基类成员和对象成员• 派生类的构造函数完成:

– 初始化派生类中的基类成员:调用基类的构造函数– 初始化派生类中新增成员数据:调用派生类的构造函数

• 派生类的构造函数的格式:其中,ClassName是派生类名;B1、…、Bn是基类构造函数名;a是形参表,a1、…、an是实参表。ClassName::ClassName(a) : B1(a1),…,Bn(an)

{ … }• 初始化成员列表:冒号后列举基类成员的构

造函数和对象成员的构造函数,项与项之间用逗号分隔。

• 初始化成员列表用基类名调用基类的构造函数,可省略基类成员的缺省构造函数(即无参或所有参数都带缺省值的构造函数)。

函数体初始化派生类中的其他成员数据

Page 9: 继承与类的派生

• 例 派生类的构造函数和析构函数。#include<iostream>

using namespace std;

class B1{protected:

int x;public:

B1(int x){ this - >x=x; cout<<"基类B1的构造函数! \ n"; }~B1( ){ cout<<"基类B1的析构函数! \ n"; }

};

class B2{protected:

int y;public:

B2(int y){ this - >y=y; cout<<"基类B2的构造函数! \ n"; }~B2( ){ cout<<"基类B2的析构函数! \ n"; }

};

Page 10: 继承与类的派生

class D: public B1,public B2{protected:

int z;public:

D(int x, int y, int z): B1(x),B2(y){ this - >z=z; cout<<"派生类D的构造函数! \ n"; }~D(){ cout<<"派生类D的析构函数! \ n"; }

};int main(void){ D d(1,2,3) ; return 0; }

程序运行结果:基类B1的构造函数!基类B2的构造函数!派生类D的构造函数!派生类D的析构函数!基类B2的析构函数!基类B1的析构函数!

• 基类构造函数的调用顺序:与继承基类的顺序有关与初始化成员列表中的顺序无关

• 说明派生类的对象:先调用各基类的构造函数,后执行派生类的构造函数。若某个基类仍是派生类,则这种调用基类构造函数的过程递归进行。

• 撤消派生类的对象:析构函数的调用顺序正好与构造函数的顺序相反。

Page 11: 继承与类的派生

• 派生类含对象成员:其构造函数的初始化成员列表既要列举基类成员的构造函数,又要列举对象成员的构造函数。

• 例 派生类中包含对象成员。#include<iostream>

using namespace std;

class B1{protected: int x;public:

B1(int x){ this - >x=x; cout<<"基类B1的构造函数! \ n"; }~B1( ){ cout<<"基类B1的析构函数! \ n"; }

};

class B2{protected: int y;public:

B2(int y){ this - >y=y; cout<<"基类B2的构造函数! \ n"; }~B2( ){ cout<<"基类B2的析构函数! \ n"; }

};

Page 12: 继承与类的派生

class D: public B1,public B2{int z;B1 b1,b2;

public:D(int x, int y, int z): B1(x),B2(y) , b1(2),b2(x+y){ this - >z=z; cout<<"派生类D的构造函数! \ n"; }~D( ){ cout<<"派生类D的析构函数! \ n"; }

};int main(void){ D d(1,2,3) ;

return 0;}

• 对象成员的构造函数的调用顺序与对象成员的说明顺序有关,而与其在初始化成员列表中的顺序无关。

• 对象成员的初始化必须使用对象名

• 基类成员的初始化必须使用基类名

• 程序运行结果:基类B1的构造函数!基类B2的构造函数!基类B1的构造函数!基类B1的构造函数!派生类D的构造函数!派生类D的析构函数!基类B1的析构函数!基类B1的析构函数!基类B2的析构函数!基类B1的析构函数!

• 问题:请写出产生上述输出结果的基类成员名或对象成员名。

• 从结果看:在创建类D的对象d时,先调用基类的构造函数,再调用对象成员的构造函数,最后执行派生类的构造函数。

Page 13: 继承与类的派生

歧义、优先规则和赋值兼容规则歧义

• 歧义:在多重继承的派生类中,使用不同基类中的同名成员所出现的现象。

• 例#include<iostream>

using namespace std;

class A{protected:

int x;public:

void Show( ) { cout<<"x="<<x<<'\ n'; }A(int a=0){ x=a; }

};

Page 14: 继承与类的派生

class B{protected:

int x ;public:

void Show( ) { cout<<"x="<<x<<'\ n'; }B(int a=0){ x=a; }

};

class C:public A,public B{protected:

int y;public:

int&AccessX ( ){ return x ; }int&AccessY ( ){ return y; }

};

int main(void){ C c; c. Show( ) ; return 0; }

• 用VC6编译时,对成员数据x有如下错误和警告:error C2385: 'C::x' is ambiguouswarning C4385: could be the 'x' in base 'A' of class 'C'warning C4385: or the 'x' in base 'B' of class 'C'

• 用VC6编译时,对成员函数Show( )有如下错误和警告:

error C2385: 'C::Show' is ambiguouswarning C4385: could be the 'Show' in

base 'A' of class 'C'warning C4385: or the 'Show' in base

'B' of class 'C'

Page 15: 继承与类的派生

• 解决歧义的方法:使各基类中的成员名各不相同。将基类的成员数据的访问权限说明为 private,并

在相应的基类中提供成员函数访问这些成员数据,但并不实用。原因是,在实际编程时,通常将基类成员数据的访问权限定义为 protected,以保障类的封装性和便于派生类访问。

用类名限定来指明所访问的成员。格式为:类名 :: 成员名

Page 16: 继承与类的派生

• 例 用类名限定来指明所访问的成员。#include<iostream>

using namespace std;

class A{protected:

int x;public:

void Show( ){ cout<<"x="<<x<<'\ n'; }A(int a=0){ x=a; }

};

class B{protected:

int x;public:

void Show( ){ cout<<"x="<<x<<'\ n'; }B(int a=0){ x=a; }

};

Page 17: 继承与类的派生

class C:public A,public B{protected:

int y;public:

int& AccessAX ( ){ return A:: x; }int& AccessBX ( ){ return B:: x; }int& AccessY ( ){ return y; }

};

int main(void){ C c;

c.AccessAX( )=1;c.AccessBX( )=2;c.AccessY( )=3;c. A:: Show( ); // 调用类A中的成员函数c. B:: Show( ); // 调用类B中的成员函数cout<<"y="<<c.AccessY( )<<' \ n';return 0;

}

• 用类名限定来指明所访问的成员,避免歧义

Page 18: 继承与类的派生

用基类成员还是对象成员?• 多个基类成员:

class B{protected:

int x;…

};class D: public B,public B {

…};

• C++规定:任一基类在派生类中只能直接继承一次,以避免上述无法避免的歧义。

• 问题:因派生类D中含有两个直接继承来的成员x,在使用类D的对象访问基类的成员x时会产生无法避免的歧义。

Page 19: 继承与类的派生

• 解决方法:组合法:在D类中,组合类B的两个对象:

class D{B b1,b2;// 若需组合更多,则可用类B的数组…

};继承+组合法:

class D: public B { // 直接继承基类B一次B b ; // 组合类B的对象…

};• 基类成员和对象成员, 在功能上是相同的,但在使用

上是有区别的 。• 通过类的继承和组合,可用已有的类来定义新类,为软件重用提供了良好机制。

Page 20: 继承与类的派生

优先规则• 优先规则: 指派生类新增成员优先于基类中的同名成

员,并不产生歧义。若要使用基类的同名成员,应在其前面加类名限定。

• 例 优先规则。#include<iostream>

using namespace std;

class A{protected:

int x;public:

A(int x){ this - >x=x; }void Show( ) { cout<<"类A的x="<<x<<'\ n'; }

};

Page 21: 继承与类的派生

class C:public A{protected:

int x;public:

C(int x, int y):A(y){ this - >x=x; }int GetX ( ){ return x ; }int GetAX ( ){ return A::x ; }void Show( ) { cout<<"类C的x="<<x<<'\ n'; }

};

int main(void){ C c(1,2);

cout<<"类C的x="<<c.GetX( )<<' \ n';cout<<"类A的x="<<c.GetAX( )<<' \ n';c.Show( );c.A::Show( );return 0;

}

• 按支配规则,此处访问的是派生类C中的新增成员。

• 加类名限定,访问的是基类A中的成员。

Page 22: 继承与类的派生

赋值兼容规则• 赋值兼容规则: 规定派生类的对象与其基类的对象之

间互相赋值的规则。设:class B{

//…};

class D: public B{//…

};

D d;B b,*pb;

Page 23: 继承与类的派生

则有以下赋值兼容规则:派生类的对象可赋给基类的对象,但反之不然。

b=d;//允许。将d中从类B继承的部分赋给bd=b;//不允许

派生类对象的地址可赋给基类型的指针变量。pb=&d;

派生类对象可初始化基类型的引用。B&rb=d;

• 注意:在后两种情况下,使用基类的指针或引用时,只能访问从相应基类中继承来的成员,而不允许访问其他基类的成员或在派生类中增加的成员。

Page 24: 继承与类的派生

多继承派生类编程实例例例 在屏幕上显示一个带有字符串的圆。问题分析 定义一个圆类 circles 和字符串类 gmessage 分别用于 在屏幕的指定位置按设定半径绘制圆和在屏幕的指 定位置显示特定的字符串。为了使所显示的圆和字 符串的位置紧密相关,采用多继承机制,从 circles

和 gmessage 派生出新类 mcircle 用于完成本例的最 终需求。另外为了更好地体现面向对象的设计思 想,再定义一个 point 类,用于确定所要显示的圆和 字符串的位置。因此 circles 和 gmessage 都应该从 point 派生。

Page 25: 继承与类的派生

⑴ 类图描述point

#x:int#y:int

point#x:int#y:int

circles#radius:int

+show()

gmessage-font:int-field:int-msg:char*+show()

mcircle

+show()

Page 26: 继承与类的派生

虚基类• 例 一个公共的基类在派生类中产生两个拷贝。

#include<iostream>using namespace std;

class A{protected:

int x;public:

A(inta=0){ x=a; }};

class B:public A{public:

B(inta=0):A(a){ }void PB( ){ cout<<"x="<<x<<'\ n'; }

};

Page 27: 继承与类的派生

class C:public A{public:

C(inta=0):A(a){ }void PC( ){ cout<<"x="<<x<<'\n'; }

};

class D:public B,public C{public:

D(int a,intb):B(a),C(b){ }void Print(void){ PB( ); PC( ); }

};

intmain(void){ D d(1,2);

d.Print( );return 0;

}

程序运行结果:x=1x=2

• 尽管一个基类在派生类中只能直接继承一次,但并未限制其在派生类中间接继承的次数。

派生类D包含两份基类A成员两份基类A成员的成员数据x的值

Page 28: 继承与类的派生

• 例 派生类包含两份基类成员,产生歧义。#include<iostream>

using namespace std;

class A{protected:

int x;public:

A(inta=0){ x=a; }};

class B:public A{public:

B(inta=0):A(a){ }};

class C:public A{public:

C(inta=0):A(a){ }};

Page 29: 继承与类的派生

class D:public B,public C{public:

D(int a,intb):B(a),C(b){ }void Print(void){ cout<< x <<'\n';

cout<< x <<'\n';}

};

intmain(void){ D d(1,2);

d.Print( );return 0;

}

• 歧义:此时编译器无法确定成员x是继承于类B,还是继承于类C。• 消除:可用类名限定来指明成员x源自类B或

类C。如可用B::x代替E行中的x,用C::x代替F行中的x。

• 问题思考:若希望类D只含一份基类A成员,则用本例实现不仅多占内存,且可能产生歧义,难以保证数据一致。如何解决?

Page 30: 继承与类的派生

• 虚基类:在定义派生类时, 在继承的公共基类的类名前加关键字virtual,使得公共基类在派生类中只有一份拷贝。

• 虚基类的格式:class ClassName:virtualaccess ClassName1{ … };

或class ClassName:access virtualClassName1{ … };

Page 31: 继承与类的派生

• 例 定义虚基类,使派生类中只有基类的一个拷贝。#include<iostream>

using namespace std;

class A{protected:

int x;public:

A(int a=0){ x=a; } //L1};

class B:virtualpublic A{public:

B(int a):A(a){ }void PB( ){ cout<<"x="<<x<<'\n'; }

};

Page 32: 继承与类的派生

class C:public virtualA{public:

C(int a):A(a){ }void PC(){ cout<<"x="<<x<<'\n'; }

};

class D:public B,public C{public:

D(int a,intb):B(a),C(b){ } //L2void Print(void){ PB(); PC(); }

};

intmain(void){ D d(1,2);

d.Print( );return 0;

}

程序运行结果:x=0x=0

• 派生类D的对象d中只有基类A的一个拷贝,如图所示。当改变成员x的值时,由基类B和C中的成员函数输出的x的值是相同的。

• x的初值为何为0?。原因:系统约定,在执行类B和类C的构造函数时都不调用虚基类A的构造函数,而是在类D的构造函数中直接调用虚基类A的缺省的构造函数。

Page 33: 继承与类的派生

•注意:用虚基类进行多重派生时,若虚基类没有缺省的构造函数,则在每一个派生类的构造函数的初始化成员列表中都应有对虚基类构造函数的调用。如上面的A(1)。

• 进一步讨论:若将L1行改为A(int a){ x=a; } //L1

再用VC6编译时,将指出 L2行有错: “error C2512: ‘A::A’: no appropriate default constructor available”,即指出类A没有合适的缺省的构造函数可用。此时可将L2行改为:

D(int a,int b):B(a),C(a),A(1) //L2即在类D的构造函数的初始化成员表中增加调用虚基类A的构造函数,将类D中的x成员的初值置为1。

Page 34: 继承与类的派生

• 如果派生类继承了多个基类,基类中有虚基类和非虚基类,那么在创建该派生类的对象时,首先调用虚基类的构造函数,然后调用非虚基类的构造函数,最后调用派生类的构造函数。若虚基类又有多个,则虚基类构造函数的调用顺序取决于它们继承时的说明顺序。

• 例12.14 虚基类与非虚基类构造函数的调用顺序。#include<iostream>

using namespace std;

class A{int x;

public:A(inta=0){ x=a; cout<<"call A(int=0)\n"; }

};

Page 35: 继承与类的派生

class B{int y;

public:B(inta=0){ y=a; cout<<"call B(int=0)\n"; }

};

class C:public B,virtual public A{int z;

public:C(int a=0,intb=0):B(b+20),A(b){ z=a; cout<<"call C(int=0,int=0)\n"; }

};

intmain(void){ C c(100,200);

return 0;}

程序运行结果:call A(int=0)call B(int=0)call C(int=0,int=0)

Page 36: 继承与类的派生

虚基类 ( 抽象类 ) 小结:1 为什麽要使用虚基类 在多继承派生中一种可能产生二义性的情况: ⑴ 派生类有多个直接基类是同一个间接基类的派

生类; ⑵ 在派生类中需要访问共同间接基类的成员。 例如: class base { // 共同的间接基类 protected:

int a;

public:

base() { a = 5; }

};

Page 37: 继承与类的派生

class base1 : public base // 直接基类 1

{

public:

base1() { cout << "base1 a = " << a << endl; }

};

class base2 : public base // 直接基类 2

{

public:

base2() { cout << "base2 a = " << a << endl; }

};

Page 38: 继承与类的派生

class derived : public base1, public base2

{

public:

derived() { cout << "derived a = " << a << endl; }

// 二义性: base1::a 还是 base2::a ?

};

main()

{

derived obj;

return 0;

}

base base

base1 base2

derived

Page 39: 继承与类的派生

2 虚基类的概念 显然,解决上述二义性问题的办法是使派生类对象 层次结构中只有一个间接基类 base 实体。 C++

允许 在派生类的定义中使用关键字 virtual 将基类说明为 虚基类来实现此目的。用虚基类重新定义上例中的 直接基类: class base1 : virtual public base

{

public:

base1() { cout << "base1 a = " << a << endl; }

};

Page 40: 继承与类的派生

class base2 : virtual public base

{

public:

base2() { cout << "base2 a = " << a << endl; }

};

class derived : public base1, public base2

{

public:

derived() { cout << "derived a = " << a << endl; }

// 访问 base::a 具有唯一性

};

Page 41: 继承与类的派生

又例如,在定义图形用户界面时,可以考虑定义这

样三 个类: class port { … }; 图形显示区

class region { … }; 屏幕上的任意区域

class menu { … }; 菜单 - 选项的集合

base

Base1 Base2

derived

Page 42: 继承与类的派生

从这些类中派生出两个新类: window 类和 tools

类: class window : public port, public region { … };

class tools : public port, public menu { … };

这两个派生类还可以再派生出新类: class appwind : public window, public tools { … };

派生 appwind 的目的是为了得到一个带工具条的窗

口, 但根据上述定义得到的结果(如下图所示)是

工具条和窗口分别拥有自己的显示区,这并不是所

希望的。

Page 43: 继承与类的派生

所希望得到的是这两个对象为一个整体,只包含一 个显示区,即工具条是包含在窗口之内的。要做到 这一点需要在窗口类 window 和工具条类 tools 的定 义中将显示区类 port 定义为虚基类。因此 tools

和 window 的定义变为如下形式: class window : virtual public port, public region { … };

class tools : virtual public port, public menu { … };

Window

abc

+

Page 44: 继承与类的派生

此时窗口即变为如下图所示的形式:

使用虚基类的两点说明:⑴ 关键字 virtual 与继承方式 public , protected 和

private

的先后顺序无关紧要,它只说明是 " 虚拟派生 " 。 例如下面两个虚拟派生的声明等价。 class derived : virtual public base { … };

class derived : public virtual base { … };

Window

abc

+

Page 45: 继承与类的派生

⑵ 任何一个类都可以在作为某些派生类的虚基类的同时,又作为另一些派生类的非虚基类,例如: class b { … };

class x : virtual public b { … };

class y : virtual public b { … };

class z : public b { … };

class aa : public x, public y, public z { … };

类 aa 的对象的层次结构如图所示:

Page 46: 继承与类的派生

b b

x y z

aa

Page 47: 继承与类的派生

3 虚基类的初始化 多继承中,虚基类与非虚基类在构造函数的调用顺 序上不同。虚基类构造函数调用顺序有三个原则: ⑴ 若同一层次中同时包含虚基类和非虚基类,先调

用虚基类构造函数,再调用非虚基类的构造函数,即“同层优先”。

⑵ 若同一层次中包含多个虚基类,虚基类的构造函

数按照定义表达式中的顺序从左至右调用。 ⑶ 若虚基类是一个派生类,则仍然先调用该虚基类

的基类构造函数,再调用该虚基类的构造函数。

Page 48: 继承与类的派生

注意,一般情况下,虚基类只定义不带参数的或带

缺省参数的构造函数。否则虚基类的构造函数必须

在最终派生类构造函数的初始化表中,显式列出。

Page 49: 继承与类的派生

例例 定义一个多继承派生类 mix 用来实现在显示器屏幕上的以一个指定点为中心画弧、圆、椭圆和

矩形。 mix 的基类为弧类 arcs 、圆类 circles 、

椭圆类 ellipses 和矩形类 rectangles 。这些基类

又由一个共同的基类 point 派生而来,因此需要将

point

定义为虚基类。各类之间的关系图如下:

point

mix

arcs circles rectanglesellipses

Page 50: 继承与类的派生

不难看出,多继承机制是一把双刃剑,它虽然为类

提供了更加强大和灵活的派生定义能力,为解决某些

复杂问题提供了一种有效的手段,但同时也因为多继

承本身具有复杂的类间包含关系,引起了类的不稳定

性和加大了处理共享和二义性的难度。虚基类机制正

是为了有效地解决多继承引起的问题而引入。因此,

在 C++ 的有些编程环境中对多继承的实现做了一定的

限制,例如 Visual C++ 的 MFC 编程向导就不提供多继

承选择(但还是支持手工编程实现多继承功能的)。

Page 51: 继承与类的派生

Java 是不支持多继承机制的,所以在 Java 的类扩展

( extends )定义中只允许从一个已定义类进行扩展,

从根本上避免了 C++ 的多继承所造成的麻烦。而单继

承带来的过分限制问题是通过允许一个类可以实现

( implements )多个接口(抽象方法类)来解决的。关

于 Java 的接口和接口的实现与 C++ 的抽象类类似,将

在第六章中做简单介绍。

Page 52: 继承与类的派生

继承在软件开发中的重要意义 继承是面向对象程序设计最重要的技术之一。继承

为软件重用和建立良好的软件层次结构提供了符合认识规律和知识拓展规律的设计机制和方便、有效的实现方法。 正是继承所提供的机制和方法,使软件开发系统的厂商为各种程序设计开发的需要开发了各类实用的类库,作为用户设计开发各类软件的基础。目前在不同的系统平台上的不同软件开发系统中,用于各类软件设计开发的基类库很多,归纳它们的组成、功能、特点大致有以下几点:

Page 53: 继承与类的派生

1 为各类软件需要的通用功能提供抽象基类、模板基 类、应用基类等,用户可以从这些基类派生实现 类、创建模板实例或直接使用基类。例如 C++ 的标 准流类库、标准模板库 STL 等。

2 为各类软件框架提供构造和运行基类等,用户可以 从这些基类派生满足特定需求的类,组成特定的程 序运行框架。例如 Visual C++ MFC , java Beans 等类 库中程序框架基类。

3 为各类软件需要的专项功能,例如数据库应用、网 络通讯等,提供基类,用户可以从这些基类派生实 际应用类。例如 Visual C++ MFC 中的 ODBC 基类、 ADO 组件类等。

Page 54: 继承与类的派生

4 基类库的实现技术(静态库、动态库、组件)各有

不同,但都不允许用户修改库中的基类,避免了一

个软件的特定需求影响到其他软件对基类的使用,

提高了通用性。

5 使用基类的软件只需要对派生类新增的部分进行编

译,然后与基类一起链接,大大提高了调试程序的

效率。如果厂家修改了基类,只要基类的公用接口

不变,使用基类的派生类就无须修改,但需要重新

编译链接。

Page 55: 继承与类的派生

6 基类库的使用一般都依赖于不同的操作系统和软件

开发环境。但有些基类库已经成为某种语言标准的

一部分,仅使用这些基类库的程序在任何操作操作

系统和软件开发环境中一般都不需要修改,具有良

好的移植性,例如 C++ 的标准流类库、标准模板库

STL 等。