📄 c++ 学习心得 part 4.txt
字号:
发信人: tomlee (藏在柔顺背后你忠于自我), 信区: CPlusPlus
标 题: C++ 学习心得 Part 4
发信站: BBS 水木清华站 (Mon Feb 14 02:17:44 2005), 站内
※ Abstract Data Type(ADT╱抽象数据类型)
虽然还有一些 C++ 的基本设施,如∶ function overloading, new/delete,
namespace 等等还没介绍,但 ADT 可以说是 C++ 最重要的基础,且其他语
言机制,包括前面提到的几个基本设施,以及各种高阶的 C++ 技术,都和
ADT 有密切的关联,因此先介绍这个部份。
早期(在 template 尚未正式引入时)所谓的 C++ 面向对象编程(OOP)的
三大精神是∶
一、封装(Encapsulation)
二、继承(Inheritance)
三、动态绑定(Dynamic Binding)(台湾译为∶动态系结)
其中「封装」指的就是 ADT。比较新的分类是以 paradigm 来区分 C++ 支援
的各种主要设计思维模式∶
一、Procedural-based(程序导向)Paradigm
二、Object-based(以对象为基础的)Paradigm
三、Object-oriented(面向对象)Paradigm
四、Generic(泛型)Paradigm
第一项的「程序导向」,简单说就是 C++ 中承袭 C 的部份。至于,第二项的
object-based paradigm 指的也就是本篇的主题∶ ADT。
对其他主流语言如∶Delphi, Java, C# 等…而言,区分 object-based(ADT)
和 object-oriented(OO)这两项似乎没有必要,因为它们都是专门支持「面
向对象」设计风格的编程语言。所以,一般其他语言所谓的 OO,就是指其中
的第二项和第三项。
但对 C++ 而言,把 ADT 从 OO 中抽离出来却是有意义的。因为 ADT 不仅是
OO 的基础,它同时也是 GP 的基础。换句话说,C++ 不同于 C 的部份应该这
么看∶
静态多型(GP)
↗
ADT
↘
动态多型(OO)
「多型」(polymorphism),简单的说,就是∶「以同样的表示法(代码),
可以用来处理不同类型的对象。」
在 C++ 中,「静态多型」主要是透过 function/operator overloading 以及
template 的机制,动态多型则是由类型的继承及 virtual function 的动态
绑定来实现。
不论是静态或动态,不论是 template 或 virtual function,这些高阶 C++
技术所处理的对象,可能是内建的数据类型(如∶int, char... 等),也可
能是用户设计的 ADT(如 String, Matrix, Vector, BigInt…等)
与 Java 等纯 OO 的编程语言相较,C++ 的 ADT 显然复杂(但也迷人)得多
,因为 C++ 语言的其中一个设计目标,就是「使用户自订类型,和内建类型
一样方便。」
其实,我个人认为所谓「纯 OO」这个词句,本身是一个历史发展的误会。因
为在 template 机制及有意义的库(如 STL, boost…等)实现前,「继承」
和「虚函式」早就已累积了像 Design Patterns(DPs)这样的伟大成就。直
到今天,绝大部份的 Java 程序员仍会认为,OO 就是指「继承」和「虚函式
」。
但对一个专业的 C++ 程序员而言,我认为有必要重新审视「OO╱面向对象」
这个观念。因为最初 OO 的理念是为了要达成「软件 IC」的理想,也就是∶
模组的设计要尽可能地强内聚(cohesion)、弱耦合(coupling),以提升
模组的独立性和复用性。
因此,只要能支持这种设计精神的语言机制,就算符合「面向对象」的理念。
只不过,由于历史的因素,「动态多型」的编程技术,其发展先于「静态多型
」,所以现在一提到 OO╱面向对象,大家会立刻想到 class hierarchies(
类阶层体系),会想到虚函式,但很少人会注意到,其实 template 对达成
软件 IC 的理想,对提升模组的独立性和复用性,在不同的面向上,提供了另
一类具有高度意义、崭新的编程思维。(例如∶某些 DPs,使用 template 取
代旧式的设计,结果更为简洁、高效。)
因此,对 C++ 程序员而言,OO 的意义应该不只是动态多型,不只是虚函式及
类阶层体系,应该把静态多型也视为面向对象的一部份(至少在软件设计的精
神上);或者,至少要清楚了解一件事∶template 和 virtual function 两
者的手段虽不同,但都是实现「多型」的工具。
回到主题来,在运用各种「多型」技术,以达成软件 IC 的理想之前,自然应
该先打好根基,那就是∶ ADT。
※ ADT 的第一步∶将数据与函式视为「成员」
抽象(Abstract)这个词汇的一般意义是指「异中求同」,也就是∶在相异的
事物中,粹取出共同的部份;相反的,「具体」则是「同中取异」,就是在一
组共同拥有某项特性的事物中,分别找出各个相异的部份。
Andrew Koening 在《C++沉思录》中,对「抽象」的解释是∶「『抽象』是『
有选择的忽略』。」这个解释很有意思,「有选择的忽略」其实就是「有选择
的粹取」。但这个解释更重要的是强调了不必在乎「被忽略了的」是什么,而
把注意力集中在「被粹取出来的」部份。
编程语言所要处理的不外是数据结构与算法。在 C 语言中,相对应的抽象化机
制则是结构与函式。但结构与函式是分离的,实际上真正具备抽象能力的只有
函式。应用 Koening 的解释,调用函式的目的是为了完成某项功能,但调用端
不必管函式内部如何实现,只要专注在其 Interface 上即可。
举例而言,假设要判断某个二维空间的点是否在原点, C 语言的实现大概会像
这样∶
struct Point // C 版本
{
int x, y;
};
int IsZeroPoint(struct Point *);
一个合理的期望是∶IsZeroPoint 应当只能作用在 struct Point 所产生的对
象上,但,C 语言威力强大的转型能力却可以很轻易破坏这个设想,例如∶
struct X x; // 某个数据结构 X
...
void *p = &x;
...
if (IsZeroPoint(p)) // 噢喔,不晓得会发生什么事
...
假如不幸的,struct X 刚好也有 x, y 两个变量,那不仅 C 编译器找不到问
题,即使在运行期,也不会发生异常。
所谓 Abstract Data Type/ADT 所指的是∶以「类型」代表某种抽象的概念(
不仅是数据),而表达某种抽象概念的一组 interface,就是该类型的成员。
因此,C++ 对类型系统的第一个改进,就是将函式与数据视为一体,也就是
class member∶
struct Point // C++ 版本
{
int x, y; // data member
bool IsZero(); // member function
};
使用上像是这样∶
Point p; // 可省略 struct
...
if (p.IsZero()) // 不但安全,而且更明确、简洁
...
C 语言也可以透过函式指针来模拟相同的功能,只是语法稍微复杂些。不过,
C++ 版本的好处不仅是语法简洁而已,更重要的是,在模组化的精神方面,
它更符合「强内聚、弱耦合」的要求。
※ class/public/private∶ 信息隐藏╱接口与实作分离
基于某些因素,有时候,我们会希望以不同的数据结构来实现 Point
struct Point
{
int X() { return data[0]; }
int Y() { return data[1]; }
int X(int x) { return data[0] = x; }
int Y(int y) { return data[1] = y; }
...
int data[2];
};
这个设计原本的目的是希望用户不要直接操作 data 数组,但用户通常不会
这么听话,因此最好的方式是让编译器拒绝用户这么做∶
struct
{
int X();
int Y();
...
private :
int data[2];
};
或
class
{
public :
int X();
int Y();
...
private :
int data[2];
};
在 C++ 中,class 和 struct 的差别只在预设存取的层级,前者是 private
,后者是 public。
这种设计的主要精神╱优点在于∶
接口以函式来实现,可以完成更复杂的功能(例如可以做条件检查)。即使
函式内部的实作改变(例如检查的条件改变了),甚至 class 内部的数据结构
改变(例如原本两个 int 变量换成数组),接口仍可保持一致。这就是所谓的
「信息隐藏」╱实作与接口分离,它最大的好处就是保持用户端的「一致性」。
依照 Koening 的讲法,private 的部份,就是用户「有选择的忽略」!
(待续…)
--
※ 修改:·tomlee 于 Feb 14 07:11:42 修改本文·[FROM: 61.57.173.*]
※ 来源:·BBS 水木清华站 smth.org·[FROM: 61.57.173.*]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -