4.1 面向对象程序设计的基本特点
4.4.1 抽象
抽象是对具体对象(问题)进行概括,抽出这一类对象的公共性质并加以描述的过程。
首先注意的是问题的本质及描述,其次是解决问题的具体过程
对一个问题的抽象应该包括两个方面:数据抽象和行为抽象(或称为功能抽象、代码抽象)
- 数据抽象:描述某类对象的属性或状态(对象相互区别的物理量)。
- 代码抽象:描述某类对象的共有的行为特征或具有的功能。
抽象的实现:通过类的声明
抽象实例——钟表
4.4.2 封装
将抽象出的数据成员、代码成员相结合,将它们视为一个整体。
- 目的是曾强安全性和简化编程,使用者不必了解具体的实现细节,而只需要通过外部接口,以特定的访问权限,来使用类的成员。
- 实现封装:类声明中的{}
4.4.3 继承
是C++中支持层次分类的一种机制,允许程序员在保持原有类特性的基础上,进行更具体的说明。
实现:声明派生类——第七章
4.4.4 多态
从广义上说,多态性是指一段程序能够处理多种类型对象的能力。在C++语言中,这种多态性可以通过强制多态、重载多态、类型参数化多态、包含多态4种形式来实现。
- 多态:同一名称,不同的功能实现方式。
- 目的:达到行为标识统一,减少程序中标识符的个数。
- 实现:重载函数和虚函数——第八章
4.2 类和对象
- 类是具有相同属性和行为的一组对象的集合,它为属于该类的全部对象提供了统一的抽象描述,其内部包括属性和行为两个主要部分。
- 利用类可以实现数据的封装、隐藏、继承与派生。
- 利用类易于编写大型复杂程序,其模块化程度比C中采用函数更高。
4.2.1 类的定义
类是一种用户自定义类型,声明形式:
4.2.2 类成员的访问控制
公有类型成员:在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。
私有类型成员:在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。 如果紧跟在类名称的后面声明私有成员,则关键字private可以省略。
保护类型:与private类似,其差别表现在继承与派生时对派生类的影响不同,见第七章。
4.2.3 对象
类的对象是该类的某一特定实体,即类类型的变量。
- 类中成员互访-----直接使用成员名
- 类外访问--------使用“对象名.成员名”方式访问 public 属性的成员
4.2.4 类的成员函数
成员函数:
- 在类中说明原形,可以在类外给出函数体实现,并在函数名前使用类名加以限定。也可以直接在类中给出函数体,形成内联成员函数。
- 允许声明重载函数和带默认形参值的函数
1、成员函数的实现
成员数据:与一般的变量声明相同,但需要将它放在类的声明体中。
原型说明了函数的参数表和返回值类型,而函数的具体实现是写在类定义之外的。与普通函数不同的是,实现成员函数时要指明类的名称,具体形式为:
调用一个成员函数与调用普通函数的差异在于,需要使用“."操作符指出调用所针对的对象,这一对象在本次调用中称为目的对象。
例如使用myClock.showTime()调用showTime函数时,myClock就是这一调用过程中的目的对象。
3、带默认形参值的成员函数
如果调用这个函数时没有给出实参,就会按照默认形参值将时钟设置到午夜零点。
4、内联成员函数:
- 为了提高运行时的效率,对于较简单的函数可以声明为内联形式。
- 内联函数体中不要有复杂结构(如循环语句和switch语句)。
- 在类中声明内联成员函数的方式:
- 隐式声明:将函数体放在类的声明中。
- 显式声明。:使用inline关键字
例:内联成员函数
4.2.5 程序实例
例:时钟类的完整程序
4.3 构造函数和析构函数
4.3.1 构造函数
- 构造函数的作用是在对象被创建时使用特定的值构造对象,或者说将对象初始化为一个特定的状态。
- 在对象创建时由系统自动调用。
- 如果程序中未声明,则系统自动产生出一个默认形式的构造函数
- 允许为内联函数、重载函数、带默认形参值的函数
例:
作为类的成员函数,构造函数可以直接访问类的所有数据成员,可以是内联函数,可以带有参数表,可以带默认的形参值,也可以重载.
4.3.2 复制构造函数(拷贝构造函数)
拷贝构造函数是一种特殊的构造函数,其形参为本类的对象引用。
其作用是使用一个已经存在的对象(由复制构造函数的参数指定),去初始化同类的一个新对象。
如果程序员没有定义类的复制构造函数,系统就会在必要时自动生成一个隐含的复制构造函数。这个隐含的复制构造函数的功能是,把初始值对象的每个数据成员的值都复制到新建立的对象中。
例:
普通构造函数是在对象创建时被调用,而复制构造函数在以下3种情况下都会被调用。
(1)当用类的一个对象去初始化该类的另一个对象时。例如:
(2)如果函数的形参是类的对象,调用函数时,进行形参和实参结合时。例如:
由于这一原因,传递比较大的对象时,传递引用会比传值的效率高很多。
(3)如果函数的返回值是类的对象,函数执行完成返回调用者时。例如:
例:完整程序
简单来说,析构函数与构造函数的作用几乎正好相反,它用来完成对象被删除前的一些清理工作,也就是专门做扫尾工作的。
析构函数是在对象的生存期即将结束的时刻被自动调用的。它的调用完成之后,对象也就消失了,相应的内存空间也被释放。
与构造函数一样,析构函数通常也是类的一个公有函数成员,它的名称是由类名前面加“~”构成,没有返回值。
和构造函数不同的是析构函数不接收任何参数,但可以是虚函数(第8章)。
如果不进行显式说明,系统也会生成一个函数体为空的隐含析构函数。
例:
4.3.4 程序实例
例题:游泳池改造预算,Circle类
一圆型游泳池如图所示,现在需在其周围建一圆型过道,并在其四周围上栅栏。栅栏价格为35元/米,过道造价为20元/平方米。过道宽度为3米,游泳池半径由键盘输入。要求编程计算并输出过道和栅栏的造价。
1 #include <iostream> 2 using namespace std; 3 const float PI = 3.14159; 4 const float FencePrice = 35; 5 const float ConcretePrice = 20; 6 7 //声明类Circle 及其数据和方法 8 class Circle 9 { 10 private: 11 float radius; 12 13 public: 14 Circle(float r); //构造函数 15 16 float Circumference() const; //圆周长 17 float Area() const; //圆面积 18 }; 19 // 类的实现 20 // 构造函数初始化数据成员radius 21 Circle::Circle(float r) 22 { 23 radius=r; 24 } 25 26 // 计算圆的周长 27 float Circle::Circumference() const 28 { 29 return 2 * PI * radius; 30 } 31 32 // 计算圆的面积 33 float Circle::Area() const 34 { 35 return PI * radius * radius; 36 } 37 void main () 38 { 39 float radius; 40 float FenceCost, ConcreteCost; 41 42 // 提示用户输入半径 43 cout<<"Enter the radius of the pool: "; 44 cin>>radius; 45 46 // 声明 Circle 对象 47 Circle Pool(radius); 48 Circle PoolRim(radius + 3); 49 // 计算栅栏造价并输出 50 FenceCost = PoolRim.Circumference() * FencePrice; 51 cout << "Fencing Cost is ¥" << FenceCost << endl; 52 53 // 计算过道造价并输出 54 ConcreteCost = (PoolRim.Area()-Pool.Area())*ConcretePrice; 55 cout << "Concrete Cost is ¥" << ConcreteCost << endl; 56 }
4.4 类的组合
4.4.1 组合
举例:
当创建类的对象时,如果这个类具有内嵌对象成员,那么各个内嵌对象将首先被自动创建。因为部件对象是复杂对象的一部分,因此,在创建对象时既要对本类的基本类型数据成员进行初始化,又要对内嵌对象成员进行初始化。
声明形式:
- 构造函数调用顺序:先调用内嵌对象的构造函数(按内嵌时的声明顺序,先声明者先构造)。然后调用本类的构造函数。(析构函数的调用顺序相反)
- 若调用默认构造函数(即无形参的),则内嵌对象的初始化也将调用相应的默认构造函数。
4.4.2 向前引用声明
- 类应该先声明,后使用
- 如果需要在某个类的声明之前,引用该类,则应进行前向引用声明。
- 前向引用声明只为程序引入一个标识符,但具体声明在其它地方。
使用前向引用声明虽然可以解决一些问题,但它并不是万能的。需要注意的是,尽管使用了前向引用声明,但是在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象。请看下面的程序段:
4.5 UML图形标识
4.5.1 UML简介
4.5.2 UML类图
4.6 结构体和联合体
4.6.1 结构体
结构:是由不同数据类型的数据组成的集合体
举例:
结构体变量形式:
结构名 结构变量名;
注意:
- 结构变量的存储类型概念、它的寿命、可见性及使用范围与普通变量完全一致。
- 结构变量说明在结构类型声明之后,二者也可同时进行。
- 结构变量占内存大小可用 sizeof 运算求出: sizeof(运算量)
初始化: 说明结构变量的同时可以直接设置初值。
使用: 结构体成员的引用形式: 结构变量名.成员名
例:
4.6.2 联合体
例子:
无名联合体
无名联合没有标记名,只是声明一个成员项的集合,这些成员项具有相同的内存地址,可以由成员项的名字直接访问。
例:
i=10;
f=2.2;