在平时说到面向对象的特性的时候,一般的答案有几种。
如果是面向对象的3大基本特征:①抽象,继承,多态 ② 封装,继承,多态
如果是4特征:抽象,封装,继承,多态
可见抽象与封装这两个概念其实联系是很紧密的。
而我确实想要搞清楚具体的含义,今天进行一个总结。
参考书目:《代码大全 2th》《C++ Primer 5th》以及网上的一些资料。
其中关于抽象与封装,以代码大全为主要参考。
关于多态以C++ Primer为主要参考。
其中我引用原文的地方都会首先标出,我开始自己总结时又会空一行开始。
内容为:抽象与封装,C++的多态。
1.抽象与封装
主要参考的是代码大全5,6两章。
5.3节---形成一致抽象
原文:抽象是一种能让你在关注某一概念的同时可以放心地忽略其中一些细节的能力——在不同的层次处理不同的细节。
5.3节---封装实现细节
原文:封装填补了抽象留下的空白。
6.1节---抽象数据类型
原文:抽象数据类型(ADT)是指一些数据以及对这些数据所进行的操作的集合。
6.2节---良好的封装
原文:封装是一个比抽象更强的概念。抽象通过提供一个可以让你忽略实现细节的模型来管理复杂度,而封装则强制阻止你看到细节。
这两个概念之所以相关,是因为没有封装时,抽象往往很容易被打破。
书中一个准则是,当我们在操作数据的时候,不应该用对象直接对数据进行访问,我们应该使用与数据对应的那些操作去操纵数据。
而这些操作(子程序)如果对于外部可见,就是类的公开接口。
而公开接口就是一种抽象,它避免了我们在数据结构的低层次上操纵数据。
因此,我的理解是
我们在定义一个类的时候,设计了类的数据成员(private),方法,并规定了哪些方法public,哪些方法private。
这个过程本身既包括了抽象,也包括了封装。
如果一定要进行一个划分,那么
抽象:将数据,方法整合到一个类里面,并且希望使用者只使用方法去操作数据成员,而且希望哪些方法应该被类使用者调用。
封装:然后在此基础上,物理上规定哪些成员为public,哪些成员为private。
所以作者说的,让我们在不同层次上处理细节。我的理解是:
假如说有一个聚会人员名单,你使用一个queue来保存每一个报名者,那么你对人员的增删就需要对于这个queue来操作。这是具体的数据结构层次。
如果你抽象为ADT,那么使用者就对聚会人员这个对象直接add(某人),或者delete(某人)这样的操作,使用者也不知道是用vector,还是queue,还是stack来存的。
作者说的,封装填补了抽象留下的空白。我的理解是:
封装保证了抽象想要达到的目的的实现。因为封装在抽象的基础上,规定了public,private。这样强制规定了可见性。这就是填补了空白的意思。
2.多态
C++的多态广义上来说有:静态多态性,动态多态性。
静态多态性通常称为编译时多态,通过函数重载来实现。
动态多态性通常称为运行时多态,通过虚函数来实现。
在面向对象的范畴来说的多态特征,是指动态多态性。这个特性在C++ Primer 5th中被直接描述为动态绑定(dynamic binding)或者运行时绑定(run-time binding)。
15.3节---虚函数
原文:引用或指针的静态类型与动态类型不同这一事实正是C++语言支持多态性的根本所在。
通过对象进行的函数调用将在编译时绑定到该对象所属类的函数版本上。
当且仅当对通过指针或者引用调用虚函数时,才会在运行时解析该调用,也只有在这种情况下对象的动态类型才有可能与静态类型不同。
15章术语表
原文:动态类型(dynamic type)对象在运行时的类型。引用所引用对象或者指针所指对象的动态类型可能与该引用或指针的静态类型不同。
基类的指针或引用可以指向一个派生类对象。
在这样的情况下,静态类型是基类的引用(或指针),而动态类型是派生类的引用(或指针)。
动态绑定(dynamic binding)直到运行时才确定到底执行函数的哪个版本。
在C++语言中,动态绑定的意思是在运行时根据引用或指针所绑定对象的实际类型来选择执行虚函数的某一个版本。
集合术语来看,书中讲的已经非常明白了。因为本身在继承的基础上就存在静态与动态类型2种类型。
动态绑定的两个必要条件就是:
①基类中必须使用虚函数或纯虚函数。
②调用函数时要使用基类的指针或者引用。