– 杂谈而已,徒然博君一笑
首先要明确的是,就能力来讲,面向对象不比面向过程更强大。新的编程模式只是选择用 一丢丢性能 或者其他换取 易扩展性、健壮性等 东西
试想我们是从面向过程向面向对象转变的程序🦍
面向对象三大特征
- 封装
- 继承
- 多态
封装
封装的意义在于 为具有一定关系的数据集合命名
于是相对于无明确相关意义的零散变量,类
、对象
作为一种新的逻辑实体 出现在我们的视野里
后边的所有东西都是围绕着 这俩货 的探讨。
PS:纯粹的封装是没有开销的。所谓封装事实上只是提供了一种 聚合数据的 作用。在引入 其他的需要运行时的时间空间开销的概念进来之前,封装只是最基本的操作。面向过程的C也还有struct呢,c只是对继承和多态不感兴趣罢了。
继承
继承分为两种,接口继承与实现继承。
继承的意义在于 代码复用,但接口继承的代码复用和实现继承的代码复用差距甚远。接口继承允许 接口的使用代码 被复用;实现继承 允许父类的代码被复用。两者可以兼容,但两者兼具时就会造成一定意义上的混淆。
就C++的成员函数而言, 基础普通成员函数就是实现继承,虚函数就是 接口继承+实现继承
实现继承
试想:当只有纯粹的 实现继承发生时,类层次之间的关系更多表现为一种 静态的组合:等于是子类拥有一个父类的实例成员。但是用户还是得把两者的代码区分开
实现继承的例子
1 | struct Base{ |
在用户看来,实现继承在使用上没有任何帮助–不能减少任何代码。(不考虑模版元编程这种只凭名字就能做到复用客户代码的技术)。
所以 实现继承是一种 模块内的技术,只是发生在子类里的代码复用而已。
另一方面,实现继承是 对类相关关系的归纳,是类间 共性的表示,对于厘清类层级有很大帮助。
接口继承
接口继承允许客户代码 复用。
- 核心:父类接口调用代码的延迟绑定
- 表现: 子类实例可以平替接口对象
看一个实例
1 | struct VBase{ |
VDerived::what()
中 使用了 继承来的 VBase::what(), 这只是举例说明 虚函数的实现也会被继承,实际上更多时候整个接口实现都会被重写,而与基类接口实现无关。
注意:doWhat 就是客户代码,接口继承允许 客户代码重用。
大型系统的开发,可以按是否依赖其他模块,区分为两部分:
- 未依赖其他模块的代码可以看作纯粹的子模块,这一部分代码不涉及系统的子模块之间的依赖,考量时的上下文可以缩减到子模块本身,于是复杂度降低。
涉及到子模块交互的代码。子模块之间的依赖的存在,就意味着客户的存在,由于子模块之间的复杂性(双向依赖),客户之间的关系错综复杂. 减少这类依赖关系带来的开发复杂性,就是接口继承的意义。
所以有种约束叫做:“规定子模块之间只能通过接口交互”
多态
泛泛而论,多态指的是 代码运行时的多种可能,对C++而言,多态就是 延迟绑定使能的 接口的运行时查表执行。
多态是与接口继承息息相关的。