C++学习之 Object Oriented

面对对象编程

面对对象编程(Object Oriented Programming)是一种特殊的设计程序的概念性方法,C++就是通过一些特性的修改,改进了C语言,使得应用这种方法更加简便。下面是最重要的OOP特性:

1。 抽象
2。 封装和数据隐藏
3。 多态
4。 继承
5。 代码的可重用性

抽象

面向对象程序设计的基本要素是抽象,程序员通过抽象来管理复杂性。

管理抽象的有效方法是使用层次式的分类特性,这种方法允许用户根据物理含义分解一个复杂的系统,把它划分成更容易管理的块。例如,一个计算机系统是一个独立的对象。而在计算机系统内部由几个子系统组成:显示器,键盘,硬盘驱动器,DVD-ROM,软盘,音响等,这些子系统每个又由专门的部件组成。关键是需要使用层次抽象来管理计算机系统(或其他任何复杂系统)的复杂性。

面向对象程序设计的本质:这些抽象的对象可以被看作具体的实体,这些实体对用来告诉我们作什么的消息进行响应。

封装

封装是一种把代码和代码所操作的数据捆绑在一起,使这两者不受外界干扰和误用的机制。封装可被理解为一种用做保护的包装器,以防止代码和数据被包装器外部所定义的其他代码任意访问。对包装器内部代码与数据的访问通过一个明确定义的接口来控制。封装代码的好处是每个人都知道怎样访问代码,进而无需考虑实现细节就能直接使用它,同时不用担心不可预料的副作用

在定义一个类时,需要指定构成该类的代码与数据。特别是,类所定义的对象叫做成员变量或实例变量。操作数据的代码叫做成员方法。方法定义怎样使用成员变量,这意味着类的行为和接口要由操作实例数据的方法来定义。

由于类的用途是封装复杂性,所以类的内部有隐藏实现复杂性的机制。所以C++提供了私有和公有的访问模式,类的公有接口代表外部的用户应该知道或可以知道的每件东西。私有的方法数据只能通过该类的成员代码来访问。这就可以确保不会发生不希望的事情。

C语言函数不是也是封装吗?

按照软件工程的要求,程序是要强调“强内聚”和“弱耦合”的,简单地说,就是强调封装,封装好了易于重用; C语言其实也强调封装,就比如函数就是C语言的一种封装机制,但函数的封装能力非常有限,只能封装某个功能,保持合理的封装无法靠机制来实现,只能靠程序员的自觉。C++正是因为C语言的封装能力不足,才产生的,C++从机制上就保证了封装的必然性,一个对象可以有多种行为,可以把相关的一组行为封装在一起,而把不相关的行为彻底与它分开。这样做从机制上保证了封装的必然性,减少了随意性。当然只有用封装的思维去使用它,C++的优势才能充分发挥。封装和重用是面向对象的核心目的,其他特点都是为这两者服务的。

继承

继承是指一个对象从另一个对象中获得属性的过程。是面向对象程序设计的三大原则之二,它支持按层次分类的概念。例如,波斯猫是猫的一种,猫又是哺乳动物的一种,哺乳动物又是动物的一种。如果不使用层次的概念,每个对象需要明确定义各自的全部特征。通过层次分类方式,一个对象只需要在它的类中定义是它成为唯一的 各个属性,然后从父类中继承它的通用属性。因此,正是由于继承机制,才使得一个对象可以成为一个通用类的一个特定实例。一个深度继承的子类将继承它在类层次中的每个祖先的所有属性。

继承与封装可以互相作用。如果一个给定的类封装了某些属性,它的任何子类将会含有同样得属性,另加各个子类所有得属性。这是面向对象程序在复杂性上呈线性而非几何增长的一个重要概念。新的子类继承其所有祖先的所有属性。子类和系统中的其他代码不会产生无法预料的交互作用。

多态

多态是指一个方法只能有一个名称,但可以有许多形态,也就是程序中可以定义多个同名的方法,用”一个接口,多个方法”来描述。可以通过方法的参数和类型引用。

面对过程和面对对象的比较

面向过程编程

面向过程编程是一种以过程为中心的编程思想,分析出解决问题的步骤,然后用函数把这些步骤一步一步实现。面向过程编程,数据和对数据的操作是分离的。

面向对象编程

面向对象编程是将事物对象化,通过对象通信来解决问题。面向对象编程,数据和对数据的操作是绑定在一起的。封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的类。它们的目的都是为了代码重用。而多态则是为了实现接口重用。面向对象的代码更加支持重用,能降低软件开发和维护的成本,提高软件的质量。掌握面向对象的困难之处在于思路的转换。我们通常习惯于考虑解决问题的方法,而不是考虑将问题抽象成对象再去解决它。

从两个简单的例子来理解两者的区别。

第一个简单的例子:编写一个驾驶汽车的方法

面向过程的程序设计:
编写一个方法,void drivecar();
面向对象的程序设计:
将一辆汽车看成一个对象,将所有汽车对象的共性抽取出来,设计一个类Car,类中有一个方法void drive(),用Car这个类实例化一个具体的对象car,调用:car。drive()

第二个简单的例子:求一个长方形的周长和面积。
面向过程的程序设计方式:
1、确定长方形周长和面积的算法。
2、编写两个方法(函数)分别计算长方形的周长和面积。
3、求周长的方法(函数)和求面积的方法(函数)需要两个参数,分别是长方形的长和宽。

面向对象的程序设计方式:
1、一个长方形可以看成一个长方形对象。
2、一个长方形对象有两个状态(长和宽)和两个行为(求周长和求面积)。
3、将所有长方形的共性抽取出来,设计一个长方形类。
4、通过长方形对象的行为,就可以求出某个具体的长方形对象的周长和面积。

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。

面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为

例如五子棋,面向过程的设计思路就是首先分析问题的步骤:1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。把上面每个步骤用分别的函数来实现,问题就解决了。

而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为 1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的i变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。

可以明显地看出,面向对象是以功能来划分问题,而不是步骤。同样是绘制棋局,这样的行为在面向过程的设计中分散在了总多步骤中,很可能出现不同的绘制版本,因为通常设计人员会考虑到实际情况进行各种各样的简化。而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。

功能上的统一保证了面向对象设计的可扩展性。比如我要加入悔棋的功能,如果要改动面向过程的设计,那么从输入到判断到显示这一连串的步骤都要改动,甚至步骤之间的循序都要进行大规模调整。如果是面向对象的话,只用改动棋盘对象就行了,棋盘系统保存了黑白双方的棋谱,简单回溯就可以了,而显示和规则判断则不用顾及,同时整个对对象功能的调用顺序都没有变化,改动只是局部的。

再比如我要把这个五子棋游戏改为围棋游戏,如果你是面向过程设计,那么五子棋的规则就分布在了你的程序的每一个角落,要改动还不如重写。但是如果你当初就是面向对象的设计,那么你只用改动规则对象就可以了,五子棋和围棋的区别不就是规则吗?(当然棋盘大小好像也不一样,但是你会觉得这是一个难题吗?直接在棋盘对象中进行一番小改动就可以了。)而下棋的大致步骤从面向对象的角度来看没有任何变化。

当然,要达到改动只是局部的需要设计的人有足够的经验,使用对象不能保证你的程序就是面向对象(C with class),初学者或者很蹩脚的程序员很可能以面向对象之虚而行面向过程之实,这样设计出来的所谓面向对象的程序很难有良好的可移植性和可扩展性。

但我觉得,初学者不必着急,就算是C with class,也不用担心,只要用心体会面向对象编程的思想,勤加练习,有一天也能写出很好的面向对象思想的程序来。(打算法题就是用C with STL我会乱说吗?)

抽象、类型、接口

抽象是什么?

生活中充满复杂性,处理复杂性的一种方法就是简化和抽象。在计算中,为了根据信息与用户之间的接口来表示它,抽象是至关重要的。也就是说,把问题的本质抽象出来,根据特征来描述解决方案。抽象是通往用户定义类型的捷径,用户定义类型指的是实现抽象接口的类设计。

什么是类?

类(class)是面向对象程序设计的核心,它实际是一种新的数据类型,也是实现抽象类型的工具,因为类是通过抽象数据类型的方法来实现的一种数据类型。类是对某一类对象的抽象;而对象是某一种类的实例,因此,类和对象是密切相关的。没有脱离对象的类,也没有不依赖于类的对象。

类是一种复杂的数据类型,它是将不同类型的数据和与这些数据相关的操作封装在一起的集合体。这有点像C语言中的结构,唯一不同的就是结构没有定义所说的“数据相关的操作”,“数据相关的操作”就是我们平常经常看到的“方法”,因此,类具有更高的抽象性,类中的数据具有隐藏性,类还具有封装性。

类的结构(也即类的组成)是用来确定一类对象的行为的,而这些行为是通过类的内部数据结构和相关的操作来确定的。这些行为是通过一种操作接口来描述的(也即平时我们所看到的类的成员函数),使用者只关心的是接口的功能(也就是我们只关心类的各个成员函数的功能),对它是如何实现的并不感兴趣。而操作接口又被称为这类对象向其他对象所提供的服务。

什么是接口?

接口(interface)是一个共享框架,供两个系统(例如计算机和打印机之间、用户和计算机程序之间)交互时使用。

对于类,就是我们说的公共接口。交互系统由类对象构成,而接口由编写类的人提供的方法组成。接口能让程序员编写类与对象交互的代码,从而让程序能够使用类对象。例如要计算string类对象包含多少个字符,就无需打开对象,只需要使用string类提供的size()方法。

类设计禁止公共用户直接访问类,但可以使用方法,例如size()等等。方法size()就是用户和string类直接的公共接口的一个组成部分。还有例如方法getline()是istream类的一个接口,使用cin的程序,不是直接与cin对象内部进行交互来读取一行输入,而是使用getline()

要使用某个类,必须了解其接口;要编写类,就必须为其创建接口。

例如你编写C++程序时,将类声明和类方法放到.h文件中,而.cpp文件中是接口的具体实现,但你只把编译好的.obj文件和.h文件发给你的用户。用户只需通过.h文件知道接口如何调用即可,不需要知道是怎么实现的。同时也防止了用户在外部对其进行修改。


参考链接

拓展阅读: