面向对象的设计(1)

<script>function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>

max()与min()函数的实现没有对数组元素的存储做特殊的假设,因此我们需要检查数

组的每个元素,如果我们要求所有的元素已经排序,则这两个操作就变得非常简单,只要索

引第一个元素和最后一个元素即可,而且如果已知元素已经排序,那么查找一个元素的存

在就会更加高效,但是对数组进行排序增加了Array 类实现的复杂性,我们的设计出错了吗?

实际上,我们现在是否犯了错误与我们做出的选择息息相关,排序的数组是一种特殊的

实现,需要时它完全必要。否则,支持排序数组的额外开销就是一项负担,我们的实现是

比较通用的,在大多数情况下是足够的,它支持更广阔范围内的用户,不幸的是如果用户

绝对需要排序数组的行为,那么我们的实现就不能提供支持,对用户来说他没有办法对

min() max()以及find()这些函数比较通用的实现进行改写。实际上,我们选择的通用实现并不适合特殊的环境。

另一方面,我们的实现对另外一类用户而言又针对性太强了,对索引的范围检查为每次

访问元素增加了额外的负担。在设计中,我们没有考虑这样的开销,而是假设如果结果不正确,那么速度再快也没有价值,但是这种设计至少对于某一类主要

用户实时虚拟和虚拟现实提供商就不成立。在这种情况下数组代表复杂3D 几何图形

的顶点,场景飞快地变化以至于一些偶然的错误不会被看到,但如果访问速度太慢了那

么实时效果就会被打破,我们实现的数组类虽然比没有范围检查的数组类会更安全,但是在这样的实时应用领域却不够实际,

我们怎样才能支持这三种用户的需要呢?解决的方案已经多多少少体现在代码中了,例

如范围检查局限在下标操作符中,去掉check.range()的调用重新命名该数组。现在我们

就有了两种实现,一个有范围检查,一个没有范围检查。进一步拷贝一份代码并把它修改

成针对已排序的数组,现在我们就有了对已排序数组的支持。

// 未排序也没有边界检查

class IntArray{ ... };

// 未排序但支持边界检查

class IntArrayRC{ ... };

// 已排序但没有边界检查

class IntSortedArray{ ... };

这种方案的缺点是什么呢

1.我们必须维护三个包含大量重复代码的数组实现,我们更希望把这些公共代码只保留

一份,然后,由这三个数组类以及其他一些我们以后会选择支持的数组类共享。比

如可能会是一个带有边界检查的排序数组

2.由于三个数组实现是完全独立的类型,所以我们必须编写独立的函数来操作它们,尽

管函数内的一般性操作都是相同的。例如

void process_array( IntArray& );

void process_array( IntArrayRC& );

void process_array( IntSortedArray& );

我们希望只编写一个函数,它不但能接受现有的数组类,而且还能够接受任意将来的

数组类。只要同样的操作集合也能够应用到这些类上,

面向对象的程序设计方法正是为我们提供了这样一种能力。上面第1 项可由继承

inheritance 机制提供。当一个IntArrayRC 类也就是一个带有范围检查的IntArray 类

继承了IntArray 类时,它就可以访问IntArray 的数据成员和成员函数。而不要求我们维护两

份代码拷贝。新的类只需提供实现其额外语义所必需的数据成员和成员函数。

在C++中,被继承的类如本例中的IntArray 被称作基类base class,新类被称作

从基类派生derived 而来,我们把它叫做基类的派生类derived class 或子类型subtype。

你可能感兴趣的:(面向对象)