5.2 基类和派生类
基类和派生类

基类和派⽣类
1.什么是基类?
在⾯向对象设计中,被定义为包含所有实体共性的class类型,被称为“基类”。
-百度百科
简单理解,即⽗类(相对派⽣类)
2.什么是派⽣类?
利⽤继承机制,新的类可以从已有的类中派⽣。
那些⽤于派⽣的类称为这些特别派⽣出的类的“基类”。
简单理解,即⼦类(相对基类)
3.两者关联
基类和派⽣类是⼀个相对的关系。
基类和派⽣类反映了类与类的继承关系,是相对⽽⾔的。
基类⼜称⽗类,是被派⽣类继承的类。
派⽣类⼜称⼦类,是从⼀个已有类的基础上创建的新类,新类包含基类的所有成员,并且还添加了⾃⼰的成员。
4.实际例⼦
假设有两个类A和B,A和B都需要实现⼀个打印的功能,原始的做法是A写⼀个打印函数,B也写⼀个打印函数。
两个类还好可以写,但多了就特别⿇烦。
这个时候我们就可以写⼀个类C,C⾥⾯写⼀个打印函数。
A和B分别继承C,这样A和B就不要写打印函数了。
这样即节省了代码,⼜优化了结构。
上⾯的情况,C是A和B的基类,A和B是C的派⽣类。
面向对象程序设计 C 复习题

一选择题(共 45题)1.对类的构造函数和析构函数描述正确的是()。
A.构造函数可以重载,析构函数不能重载√B.构造函数不能重载,析构函数可以重载C.构造函数可以重载,析构函数也可以重载D.构造函数不能重载,析构函数也不能重载2.继承具有(),即当基类本身也是某一个类派生类时,底层的派生类也会自动继承间接基类的成员。
A)规律性 B.传递性√ C.重复性 D.多样性3.假设OneClass为一个类,则该类的复制初始化构造函数的声明语句为()。
(OneClass p); B. OneClass& (OneClass p );C. OneClass(OneClass & p);√D. OneClass (OneClass * p);4.对于结构中定义的成员,其默认的访问权限为()。
B. protected D. static5.下面对静态数据成员的描述中,不正确的是()。
A.静态数据成员可以在类体内进行初始化√B.静态数据成员不可以在类体内进行初始化C.静态数据成员能受private控制符的作用D.静态数据成员调用前必须初始6. C++中的类有两种用法:一种是类的实例化,即生成类对象,并参与系统的运行;另一种是通过()派生了新的类。
A.复用B.继承√C.封装D.引用7. 假定AA为一个类,a为该类公有的数据成员,x为该类的一个对象,则访问x对象中数据成员a 的格式为()。
A. x(a)B. x[a]C. x->aD. √9. 对于一个类的构造函数,其函数名与类名( )。
A. 完全相同√B. 基本相同C. 不相同D. 无关系10. 一个类的构造函数通常被定义为该类的( )成员。
A. 公有√B. 保护C. 私有D. 友元11. 一个类的析构函数通常被定义为该类的( )成员。
A. 私有B. 保护C. 公有√D. 友元12. 一个类的静态数据成员所表示属性 ( )。
A. 是类的或对象的属性B. 只是对象的属性C. 只是类的属性√D. 类和友元的属性13.类的析构函数的作用是()。
c++第5章 图形绘制

第5章图形绘制在Visual C++6.0中,掌握图形程序设计方法是非常重要的。
因为图形在任何一个可视化工程项目中都是不可缺少的。
CDC(设备环境)类封装了图形绘制所需要的各种操作。
本章我们将通过实际例子和较详细的阐述,来了解和掌握如何使用设备环境类(CDC)及图形设备接口(GDI)进行图形绘制。
5.1 设备环境与设备环境类(CDC)5.1.1 设备环境设备环境也称设备上下文(Device Context,简称DC),是计算机物理设备的代表,也是图形设备接口的主要组成部分。
由于Windows是一个与设备无关的操作系统,即Windows不允许直接访问硬件,如果用户想将文本和图形绘制到显示器或其它设备中去,必须通过“设备环境”这个抽象层与硬件进行通信,设备上下文对象的作用就是实现Windows的设备无关性,任何向屏幕上进行输出的功能都要间接地通过它来完成。
设备上下文是Windows的一种数据结构,它包含了有关如显示器或打印机等设备的绘图属性信息。
所有绘画都是通过设备上下文对象来实现的,该对象封装了Windows的画线、图形和文本的API函数。
设备上下文允许在Windows下独立于设备的绘画。
设备上下文不仅能够被用来在屏幕上绘画,它也可以将绘画输出到打印机和图元文件中。
5.1.2 设备环境类设备环境类CDC直接继承于CObject类,该类定义了一类设备对象。
CDC对象提供了非常多的成员函数,与设备环境的显示器、打印机等一起工作。
例如,如果要在显示器等设备上绘制图形,我们可以用MFC提供的设备环境类CDC类,因为CDC类中包含了绘图所需要的所有成员函数。
同时。
MFC还提供了以下几个CDC的派生类:1、CPaintDC类此类比较特殊,它的构造函数和析构函数都是针对OnPaint进行的。
用户一旦获得相关的CDC指针,就可以将它当做任何设备环境(包括屏幕、打印机)指针来使用,CPaintDC类的构造函数会自动调用BeginPaint,而它的析构函数则会自动调用EndPaint。
组合和继承

例如有以下的类X : class X { 类名1 ob1; 类名2 ob2; … 类名n obn; };
5.1.2 对象成员的初始化
类X的构(参数表1),ob2(参数表2),…,obn(参数表n) { 类B的构造函数体 }
参数表1、参数表2 、… 、参数表n的数据,一般来自参数表 0。当类名1、类名2...类名n的构造函数不带参数或带有默认 参数时,类X的构造函数中冒号及后面的对象成员初始化列 表可省。例如:
Point::InitP; Point::GetX; … };
注意:1)在派生类中声明基类的函数时,只需给出函数的名 称,函数的参数和返回值类型不应出现。
class Rectangle: private Point{
……
public:
Point::InitP;
//正确
Point::GetX;
//正确
X∷X(参数表0) { 类B的构造函数体 }
5.1.2 对象成员的初始化
当调用构造函数X()时,首先按个对象成员在类中声明的 顺序依次调用它们的构造函数,对这些子对象初始化; 然后再执行X()的构造函数体初始化类中个其它成员。 析构函数的调用顺序与构造函数的调用顺序相反。
第5章 继承和派生类

对类B的对象初始化即是对x,y,z,m,n等全部成员的初始化
5.4.2 私有继承
私有派生,派生类中基类的公有和保 护成员成为私有
class ClassName : private BaseClassName
私有派生时,基类中公有成员和保护成员在派生类 中均变为私有,在派生类中仍可直接使用这些成员,基 类中的私有成员,在派生类中不可直接使用。
15
从一个基类派生一个类的一般格式为:
class ClassName : <Access> BaseClassName
{ private: public:
派生类名 继承方式 基类名
......; //私有成员说明
......; //公有成员说明
protected:
派生类中新增加的成员
......; //保护成员说明
C++程序设计
主讲:王新祥
第5章 继承和派生类
2
5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9
继承与派生的概念 派生类的声明方式 派生类的构成 派生类成员的访问属性 派生类的构造函数和析构函数 多重继承 基类与派生类的转换 继承与组合 继承在软件开发中的重要意义
3
继承性是面向对象程序设计中最重要的机制。这 种机制提供了无限重复利用程序资源的一种途径。通 过C++语言中的继承机制,可以扩充和完善旧的程序 以适应新的需求。这样不仅可以节省程序开发的时间 和资源,并且为将来的程序增添了新的资源。
基类:public: 在派生类和类外都可以使用 protected: 在派生类中使用 private: 不能在派生类中使用
17
class A { int x; 因为y是基类保护,所以在派生类中可以 protected: int y; 直接引用。而在类外不可直接引用。 public: int z; A(int a, int b, int c) { x=a; y=b; z=c; } //基类初始化 int Getx() { return x; } //返回x int Gety() { return y; } //返回y void ShowA() { cout<< "x="<<x<<'\t'<<"y="<<y<<'\t'<<"z="<<z<<'\n'; } }; 因为z是基类公有,所以 公有派生 class B : public A { 在 派 生类 中和 类 外均 可 对基类初始化 int m, n; 直接引用。 public: B(int a, int b, int c, int d, int e) : A(a, b, c) { m=d; n=e; } void Show() { cout<<"m="<<m<<'\t'<<"n="<<n<<'\n'; cout<<"x="<<Getx()<<'\t'<<"y="<<y<<'\t'<<"z="<<z<<'\n'; } int Sum() { return (Getx()+y+z+m+n); } }; 因为x是基类私有,所以在派生类中和类外不能直接引用 void main(void) { B b1(1, 2, 3, 4, 5); b1.ShowA(); b1.Show(); cout<<"Sum="<<b1.Sum()<<'\n';cout<<"x="<<b1.Getx()<<'\t'; cout<<"y="<<b1.Gety()<<'\t'; cout<< "z="<<b1.z<<'\n'; } 18
基类和派生类

5
(1)公有继承(public)
当类的继承方式为公有继承时,基类的公有和保 护成员的访问属性在派生类中不变,而基类的私有 成员不可访问,
即基类的公有成员和保护成员被继承到派生类中 仍作为派生类的公有成员和保护成员,派生类的其 他成员可以直接访问它们。
具体赋值规则如下: 派生类的对象可以赋值给基类对象。 派生类的对象可以初始化基类对象的引用。 派生类对象的地址可以赋给指向基类对象的 指针。
15
例如: class A{...};
class B: public A
{void fun() {...}}; A a; B b; a=b; //将对象b中所含类A成员的值赋给对象a A &a1=b; //用派生类对象初始化基类对象的引用 A *pa =&b;
22
基类名1,继承方式2 基类名2,… {…} ;
{ 派生类类体
class C : public
};
A , private B
多重继承允许一个派生类同时继 {…} ; // C 公有
承多个基类中的成员,支持了软件 继承 A,私有继
的重用性,但也可能带来大量的二 承 B
义性问题。 17
复合与继承在软件渐增式开发中的应用
10
3.在派生类中重定义基类的函数
派生类自动继承了基类中定义的数据成员和成员函数。 如果派生类认为基类中某个成员函数的实现不能满足需 要,可以在派生类中重新定义该函数。
重定义基类的成员函数需要使用和该函数相同的函数 名和参数列表,如果参数列表不同,就是函数重载而不 是函数的重定义了。
第5章 继承和派生类

2010/9/18
第5章 继承和派生类
程序运行结果: x=4 y=5 l=6 w=8 程序分析: 通过公有继承方式,从BaseClas类得到DerivedClass类。 派生类DerivedClass只有一个基类,所以是单继承。基类 BaseClass定义了两个数据成员,3个成员函数。派生类 DerivedClass定义了两个数据成员,3个成员函数。通过 继承,派生类DerivedClass拥有4个数据成员:x、y、l和 w,6个成员函数:setx ( )、sety ( )、setl ()、setw ( )、 displayxy ( )、displaylw( )。
2010/9/18
面象对象程序设计
第5章 继承和派生类
class Father:private Grandfather //中间类:Grandfather的 公有派生类, //Son的基类 { public: void test() { b1=10; //可以访问,因b1改为private b2=20; //可以访问,因b2改为private b3=30; //不可访问, 因b3改为不可访问 } };
2010/9/18
面象对象程序设计
第5章 继承和派生类
5.1.2 继承的种类
在C++语言中,一个派生类既可以从一个基类派生,也可 以从多个基类派生。从一个基类派生的继承被称为单继承。 单继承形成的类层次是一个倒挂的树,如图5.2所示。从 多个基类派生类的继承被称为多继承。多继承形成的类层 次是一个有向无环图(DAG),如图5.3所示。在图5.2中, 输入设备是基类,从它派生出了3个派生类:键盘类、鼠 标类和扫描仪类。在图5.3中,从教师类和干部类派生出 校长类,即校长类有两个基类。
第5章 派生类与继承

void print();
};
print();
};
//定义一个基类 class person{ protected: char name[10]; int age; char sex; public: //…… }; //定义一个派生类 class employee:public person { protected: char department[20]; float salary; public: //…… };
当类的继承方式为私有继承时,基类的public成员和 protected成员被继承后作为派生类的private成员,派生类 的其他成员可以直接访问它们,但是在类外部通过派生类的 对象无法访问。
基类的private成员在私有派生类中是不可直接访问的, 所以无论是派生类成员还是通过派生类的对象,都无法直接 访问从基类继承来的private成员,但是可以通过基类提供 的public成员函数间接访问。 例5.1一个私有继承的例子
由于派生类继承了基类的成员,派生类的构造 函数需要调用基类的构造函数对其中定义于基 类的数据成员进行初始化。 给基类构造函数传递实际参数是通过向派生类 构造函数传递实际参数以及初始化列表来间接 实现传递的。
5.2.1 派生类构造函数和析构函数的执行顺 序 通常情况下,当创建派生类对象时, 首先执行基类的构造函数,随后再执行 派生类的构造函数; 当撤消派生类对象时,则先执行派生 类的析构函数,随后再执行基类的析构 函数。
例5.3 公有继承的访问规则
表5-3 公有继承的访问规则
基类成员 内部访问 对象访问 Private成员 不可访问 不可访问 public成员 可访问 可访问 protected成员 可访问 不可访问
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
5.2 基类和派生类
在C++中,当一个类被其他类继承时,被继承的类称为基类(base class)。
继承其他类特性的类称为派生类(derived class)。
从本质上看,基类是具有一个类集合中的公共特性,派生类在继承基类特性的同时可以加入自己独有的特性。
基类与派生类之间反映出下述三种不同的现象:
(1)派生类是基类的具体化。
即模拟概念层次,表示“is-a”的关系。
(2)派生类是基类的延迟定义。
可以定义一个抽象基类,定义一些操作,使它们服从一定的协议,但许多可能并未实现,然后定义非抽象的派类,实现抽象基类中定义的行为。
这时派生类不是基类的具体化,而是抽象类的实现。
在JA V A中,有专门的纯虚类,称为接口,其作用就是为不同的类提供一个统一的接口,同时间接实现多继承(JA V A不支持多继承)。
(3)派生类是基类的结合。
当一个派生类有多于一个的基类时,它们组合在一起形成具有所有基类行为的类型。
这时要注意,不要用继承表达聚合关系。
5.2.1 基类与派生类的说明
先看一个例子。
[例5.1] 派生类的说明EX5_1.CPP。
继承基类的派生类定义的一般形式:
class derived_class_name:access_specifier base_class_name
{
……
};
其中access_specifier可以是3个关键字之一:public、private(默认值)或protected。
派生类也称为子类、导出类。
它具有下述特点:
(1)可在基类所提供有基础上包含新成员;
(2)可在自己类中隐藏基类的任何成员;
(3)为新类重新定义基类中的函数;
[例5.2] 子类的特点EX5_2.CPP。
5.2.2 派生类的继承权与访问域
派生类的继承权如果不能有效在加以限制,就不能按照实际情况表达求解问题的复杂性。
因此访问权限是一个很重要的问题。
(1)对于基类的私有成员,派生类及派生类的使用者无权访问。
(2)对于基类的公有成员,则按派生类的定义,分为三种情况:
①私有派生,继承基类的公有成员作为自己的私有成员,这些成员只能被派生类的成员函数访问。
Access_specifier是private或省略。
[例5.3] 私有派生EX5_3.CPP。
②公有派生是基类中所有的公有成员,在派生类中也都是公有的。
它不必一一说明,而在派生类定义时,在基类前加一个public 关键字。
如:
class:public b
{
……
};
③保护派生,基类的公有成员和保护成员在派生类中是保护成员,仅能在派生类的成员中被使用,而不允许派生类的对象使用。
Access_specifier是protected。
表5.1 列出了成员访问控制的各种情况。
(3)派生类对基类成员直接访问问题。
派生类不能访问基类的私有成员,若要访问必须使用基类的接口,即通过基成员函数。
如何直接访问类成员,有两种方法可选:
①在类定义体中增加保护段(protected),将基类私有成员提供派生类访问的部分放置在保护段。
②将需要访问基类私有成员的派生类成员函数声明为基类的友元。
[例5.4] 派生类对基类成员的直接访问EX5_4.CPP。
(?)
(4)访问域的调整规则
使用作用域符(::)可以调整访问域,但要注意其限制条件:
①访问声明只能对变量或函数名,不能说明类型和参数;重载函数只需一个声明即可。
②不能对私有段成员作访问声明,必须保护封装性。
③只能在相应的段(保护或公有段)作访问声明,不能改变所属段。
即基类成员被调整后,在派生类中的访问权限既不能扩大也不能缩小。
基类中的公有成员只能被调整为公有成员,保护成员只能被调整为保护成员,私有成员不可调整。
[例5.5] 访问域的调整EX5_5.CPP。
class base {
public:
int a;
protected:
int b;
private:
int c;
};
class derived:base{
public:
base::a;//correct
base::c;//error
base::b;//error
protected:
base::b;//correct
base::a;//error
base::c;//error
};
若基类和派生类具有同名成员,则基类的该成员被编译器隐藏起来。
若在派生类中调用继承来的被隐蔽的成员,作为访问声明需要明确指定基类类范围;而非同名成员,派生类在使用继承成员时可视为是在该派生类范围,且不必对这些成员重新定义。
5.2.3 派生类的构造函数和析构函数
基类往往有构造函数和析构函数,但创建和结束派生类对象时,如何调用它们,这是下面需要讨论的问题。
(1)执行原则:当基类和派生类都具有构造函数和析构函数时,将按类派生的顺序执行构造函数,而按相反的顺序执行析构函数。
(2)派生类构造函数:当派生类本身需要构造函数,或者是在定义派生类对象时,其相应的基类对象需要调用带参数的构造函数,就必须定义派生类的构造函数。
定义格式如下:
derived_constructor(arg_list):base(arg_list)
{
//body of derived constructor
};
这里建立了一个变量传递链,首先将基类和派生类所需的所有变量都传递给构造函数。
然后,用派生类构造函数的扩展说明形式,将某些变量传递给基类。
[例5.6] 从派生类向基类传递变量EX5_6.CPP。
(3)有关说明
若基类使用缺省构造函数或不带参数的构造函数,则在派生类中定义构造函数时可略去“:base(arg_list)”,此时若派生类还需要初始化,则可不定义构造函数。
派生类是否要定义析构函数与所属基类无关。
若派生类在退出其定义域前需要作释放内存等处理,就需要定义析构函数。
[例5.7] 定义学校人事管理的类及其调用情况显示实例EX5_5.CPP。