导图社区 Cplusplus面向对象基础
"C plus plus" 即"C++"。C++,这个词在中国大陆的程序员圈子中通常被读做"C加加",而西方的程序员通常读做"C plus plus",它是一种使用非常广泛的计算机编程语言。
编辑于2021-08-18 11:44:12C++面向对象基础(上)
对象与类
对象
属性
数据
闹钟(时间)
行为
函数
闹钟(修改时间或预定时间)
程序中的对象是一个整体,其属性和行为不可分离。要访问对象的属性(数据)和行为(函数)只能通过对象进行。例如要查看闹钟的“当前时间”或“预定时间”,只能通过闹钟这个对象进行,指示时间和响铃也只能通过闹钟进行。
好处是可以对相关的数据和函数按照需要分类管理,从而提高程序代码的可读性,以及软件的可维护性。
示例
闹钟对象
先定义一个表示时间的结构体Time
int h
int m
int s
Time 的两个变量
当前时间
预定时间
属性
定义两个函数
指示时间函数
到时响铃函数
行为
类
对象是一个完整的个体
这些个体可以按照某种规则进行分类
分类的规则是对象的属性相同(数量相同,类型也相同),行为也相同。
闹钟类
属性相同
形状
当前时间
预定时间
属性个数相同,相同的属性的类型也相同
行为相同
当前时间
到时响铃
闹钟类与闹钟对象
同一类的不同对象是靠属性值区别的
同一类的两个对象的属性值完全相同,则两个对象就是相同的
对象的表示
class
用了特殊的数据结构类(class)来集成数据和函数
在 C 语言中结构体虽然可以用来集成数据,但不能用来集成函数。
而类的变量就是对象。同结构体一样,类也是可以自定义的,即开发者可以设置类中包含的数据以及函数。
类与对象的关系就是类型与变量的关系。
在程序中要使用一个对象,就用这个类声明一个变量,这个变量就是所需的对象。
闹钟示例
声明并使用一个闹钟对象
对象的特征
封装性
但是对于对象的使用者而言,并不能访问这个集合中的所有部分。使用者只能访问对象暴露出来的、可以被外部使用的部分,这些部分也可以看做对象的接口。而其他未暴露出来的部分则是对象的私有属性或者私有函数。私有属性和私有函数是为了实现对象的接口而存在的。
闹钟的私有属性和私有函数
程序中的对象具有封装性,这一点同现实中的对象是一致的。人们在使用闹钟时,根本不需要知道其内部是怎么实现的,而设计闹钟的人也无意向用户暴露其中的细节。
继承性
继承是一种逻辑上的层次关系。在人类的思维中,有这样一种层次性的分类方法,即将一些事物按照一般到特殊的关系
先分成大类,再分成小类,各个小类也可以继续细分。
大类与小类就构成了一种逻辑上的层次关系。大类(高层的类)称做基类(或者父类),小类(低层的类)称做派生类(或者子类)。
示例
学校类具有所有学校的共同属性,如校名、校址等,以及共同的行为,如招生、考试、教育等
但这些并不符合所有学校的情况,比如在招生的范围、教育的方式、考试的内容等方面都不相同。
继承还有另外一层重要的含义,即派生类中具有基类的所有组成部分。因为从基类到派生类是从一般到特殊,“特殊”只是比“一般”多了一些属性和行为,而不会缺少。
从这个意义上讲,派生类“是”一个基类,例如大学是学校,中学是学校,小学也是学校。
优点
派生类可以使用基类的方法和属性
对基类的操作也可以施加到派生类上
多态性
多态性指的是同一行为在基类和派生类间,以及各个派生类间的表现并不相同。
这里所说的同一行为,指的是在基类里定义并由各个派生类继承的函数。
由于基类在定义这个函数时考虑的是一般的情况,并没有针对各种特殊情形,所以派生类往往需要重新定义这个函数。
当系统运行时,虽然使用的是同一个函数名,但具体调用的函数却是被派生类重新定义的版本。由于不同的派生类有不同的定义,从而一个行为就会表现出不同的形式,也就是多态。
示例
例如对于各种学校类,作为学校的基本功能,定义基类“学校类”时就应当定义招生、教育、考试等各种行为。但对于各种具体的学校类,如小学、中学、大学等,这些行为又是有差别的,必须根据各自的具体情况进行重新定义。当说一个学校招生时,不同的学校有不同的招生对象、方法等,所以招生这个行为就在不同种类的学校间表现出了多态性。
面向过程与面向对象
面向过程
在面向过程的开发中,开发者关注的是解决问题过程中每一个步骤的实现以及步骤的次序。只有实现好每个步骤,并且按照正确的次序进行调用,问题才会得到解决。这就是所谓的过程。
在C和C++语言中,一个步骤就是一个函数或者一个语句块(复合语句)。
缺点
采用面向过程方法开发的程序,难以控制其维护成本。
缺少封装性
无法封装数据
无法封装函数
当一组数据需要几个函数联合起来进行处理的时候,要么采用全局变量,要么将数据作为参数。全局变量的缺点是显而易见的,即无法控制访问数据的行为,任何函数都有可能修改全局变量,甚至不相关的函数。如果将数据当做函数参数,则会导致函数的参数列表过于复杂,同时也会因参数传递导致效率方面的损失。
在面向过程的语言中,函数的使用是没有限制的。而在实际开发中,一个函数的目标数据是固定的。如果不限制函数的作用范围,函数与数据之间就不会有清晰的逻辑关系,从而导致程序的可读性降低。
面向对象
开发的核心
如何实现对象
首先要确定程序中需要哪些对象以及这些对象的属性和行为。
确定后,开发者就要在程序中实现这个对象,即定义对象所属的类。
完成类的定义之后,就可以在程序中用类创建对象,并通过对象间的协作来解决问题了。
如何使用对象
三角形对象求面积的接口方法很简单,是一个没有任何参数的函数。使用者并不能从该接口获知任何实现的细节。这些细节都封装在对象的内部,外界无法得知。所以,这是一个与实现无关的接口。在程序开发以及以后的维护中,只要这个接口不变,无论其内部实现如何改变,都不会影响使用者的调用,在很大程度上降低了软件的维护成本。
面向对象的分析(OOA)
在开始系统设计之前,软件开发应当经历系统分析阶段。其目的是在需求分析的基础上,描述系统的整体功能,并建立问题领域的模型。
在面向对象的软件开发中,系统分析的方法是面向对象的分析方法,简称OOA(Object Oriented Analysis)。
在传统的面向过程的软件开发中,系统分析的方法是结构化的分析方法。其特征是对系统的功能进行模块化、层次化的划分,并用数据在一系列模块间的流动表示系统行为。
在OOA过程中,对问题领域进行建模就是从对系统的分析中识别出所需的对象,并用对象和对象之间的关系来描述整个软件系统。
例如在一个窗口中绘制复杂图形,而图形可能是由多个基本图形(三角形、矩形、圆形等)构成的,在这样的系统中需要画布类、图形容器类、图形基类、各个图形类等,
OOA基本原则
抽象——抽取数据和过程特征部分。
封装——将数据和处理函数组织在一起。
分类——描述对象的类型。
继承——描述对象的概念层次结构。
is-a
聚合——用组装的方法建立对象。
聚合原则用在描述对象的组成上。一个复杂的对象往往是多种简单对象的复合体。因此,在程序中应当将简单对象作为复杂对象的数据成员(属性),从而以自然的方式描述一个复杂事物。
has-a
关联——建立对象间的联系。
关联表示的是对象之间的联系。与继承、聚合不同,关联关系比较松散,一个对象存在与否并不影响另外一个对象。
消息通信——对象间协作的方法。
消息通信原则指的是对象之间只能通过消息进行通信,而不允许直接存取对象的属性。消息通信原则是由封装引起的。封装的对象只能通过公开的接口与外界通信,而这个接口,即接口的调用行为就是所谓的消息。
粒度控制——简化信息。
粒度控制指的是在OOA中,应当将分析的主题限制在适当的范围内,不要过度深入细节。OOA的目标是要得到一个系统整体性的描述,如果过度深入细节,会使分析陷入复杂化,反而达不到预期的效果。例如,一个对象在OOA中只要给出该对象的对外接口即可,至于内部怎么实现则不考虑。
行为分析——分析对象的行为。
行为分析原则是OOA的指导性原则,其含义是要求开发者从分析问题领域中各种事物的行为入手。事物的行为是其内部属性的反映,也直接代表其对外接口。
对事物行为的分析可以得出一个对象的定义。另外,事物之间的行为存在依赖和顺序关系,而这些关系也反映了对象之间的关系。因此,经过行为分析就可以从静态和动态两个方面对系统进行建模。
OOA的基本步骤
确定系统功能。在分析开始前,首先要确定软件系统要解决哪些问题,系统要提供什么功能。
找出对象并分类。通过对系统中各种事物的抽象,得出所需要的对象,并对这些对象进行分类,建立“类”类型。
确定类之间的关系。这些关系包括继承关系、聚合关系、关联关系等。
确定对象的属性。这些属性是对象的标识性、关键性的属性,并可以被外界访问。
确定对象的函数。这些函数表示的是对象的行为。
通常会采用统一建模语言作为描述工具。统一建模语言简称UML,是一种通用的、图形化的建模方法。可以用来描述系统的静态和动态模型,包括类图、对象图、时序图、协作图、用例图等。有关UML语言的详细说明请参看相关书籍,本书中也大量应用了UML语言来对软件系统进行分析和设计。
面向对象的设计(OOD)
设计的目的是对分析的结果进一步规范和整理,以便进行后面的代码设计。在面向对象的开发中,设计的方法是面向对象的设计(Object Oriented Design),简称OOD。在OOD过程中同样也采取了很多面向对象的思想。
OOA过程的结果是一个概念性的结果,并不能直接用于OOP,即面向对象的编程。
在开始编写代码之前,需要对OOA所抽象出来的对象、类以及其他相关文档进行整理和求精,使之更符合OOP的需要。这个过程就是所谓的OOD。
OOD 的目标是将对象的属性和方法进一步精化,改正错误的内容、删去不必要和重复的内容等。有时根据需要,还应当添加一些辅助的属性和方法,这些方法仅用于辅助对象接口的实现,原则上应当不对外公开,而是作为对象的私有成员。
OOD基本原则
单一职责原则
单一职责指的是一个类应当只负责一方面的功能。单一职责的目标是简化类的定义,提高程序的可维护性。如果一个类牵涉多方面的功能,则导致修改类的原因就会很多。而一旦类被修改,其他依赖于该类的代码就需要修改,从而带来很多潜在的问题;即便不需要修改,其他依赖该类的源文件也要重新编译,影响开发效率。
开放封闭原则
开放封闭原则指的是软件系统应当对扩展开放,对修改封闭。该原则主要应用在系统需要增加功能的情况下。增加功能最好的方法就是增加新的类,而不是去修改已有的代码。后者往往会对已有的系统造成较大的冲击,从而降低软件的可维护性。
替换原则
替换原则指的是在程序中所有用到基类对象的地方,都可以用派生类对象代替。从逻辑上来讲,派生类对象和基类的关系是 is-a 的关系,即派生类对象也是一个基类的对象。既然如此,派生类的对象当然可以替代基类的对象。
依赖倒置原则
依赖倒置原则指的是程序应当依赖于高层的抽象,而不是底层的实现细节。在面向对象的开发中,类(对象)以及类(对象)间的协作关系是对现实的抽象,而基类更是对派生类的抽象。抽象的东西一般很少发生改变,因此依赖于抽象的程序其维护的成本就比较低。
接口隔离原则
接口隔离原则指的是在设计类时使用多个专门的接口,而不是一个大而全的接口。这里的接口指的是一个特殊的基类,其中仅有成员函数。应用接口分离原则,一个接口发生了改变,不至于影响到其他接口,这样依赖于其他接口的程序也不用改变,从而提高了程序的可维护性。
OOD的基本步骤
细化类。从实现的角度出发,按照功能细化类,并增加必要的辅助类。
明确类之间的关系。用继承、聚合、关联来描述类之间的关系。
定义属性。确定属性的类型,并在必要时增加或删减属性。
类图UML
描述对象之间的协作关系。描述系统动态运行的情况。
时序图UML
利用设计模式优化设计。
在面向对象的设计过程中,有一些模式可以遵循。《设计模式:可复用面向对象软件的基础》