C++抽象编程——面向对象(6)——设计新的类(1)

来源:互联网 发布:为什么一淘比淘宝便宜 编辑:程序博客网 时间:2024/05/20 20:20

虽然前面我们定义中的Point类说明了用于定义新类的基本机制,但对于面向对象的理解需要考虑更复杂的示例。所以接下来我们就以一个实例来设计一个有理数的类别,它也可以表示两个数的相除,或者我们可以称他们为分数。接下来我们一步一步的实现它。(所以下面的有理数指的是分数)

有理数与浮点数

在某些方面,有理数(Rational numbers)与我们以前一直使用的浮点数相似。两种类型的数字都可以表示分数值,如1.5,这是有理数3/2。不同之处在于,有理数是精确的,而浮点数则受到硬件精度的限制。
要了解为什么这种区别很重要,请考虑将以下分数相加的算术问题:

显然的是,数学上精确的答案是1,但是这个答案是不可能在计算机上使用双精度算术获得的。我们可以编写程序执行此计算并显示16位结果:

#include <iostream>#include <iomanip> //setprecisionusing namespace std;int main() {    double a = 1.0 / 2.0;    double b = 1.0 / 3.0;    double c = 1.0 / 6.0;    double sum = a + b + c;    cout << setprecision(16);//控制输出流显示浮点数的有效数字个数    cout << "1/2 + 1/3 + 1/6 = " << sum << endl;    cout << a << endl;    cout << b << endl;    cout << c << endl;    return 0;}

运行结果是:

我们可以看到,虽然最后结果是1,但是这个1的前提是c的值进行了四舍五入的,不然它的值是0.16666666……,因此,从double类型的角度看,如果不四舍五入,那么结果就是这样的:

问题在于,用于在计算机内存储数字的存储单元具有有限的存储容量,这反过来又限制了它们可以提供的精度。在双精度算术的限度内,加上一半加三分之一加 六分之一更接近0.9999999999999999,而不是1.0。 更糟糕的是,这个总和的计算值真的小于1,如果你要在你的程序中测试,就会显示出来。 在运行结束时,表达式sum <1的值将为真,并且sum == 1的值将为false。 从数学的角度来看,这一结果显然是不应该的。
合理的数字不会受到舍入误差的影响(比如1/2 = 0.5),因为不涉及近似值。 此外,有理数能被很好地理解为具有自己的算术规则的数学概念,问题是C++不包括其预定义类型中的有理数字。 要在C++中使用有理数字,您必须定义一个新类来表示它们。

怎么设计一个新的类?

当你以面向对象语言工作时,设计新的类是你需要掌握的最重要的技能。与大多数编程一样,设计一个新类就像一门科学一样的艺术。开发有效的新的类的设计需要强烈的美学意识和对使用这些类作为工具的客户的需求要相当敏感。经验和实践是最好的老师,但遵循一般的设计框架可以帮助你沿着这条路开始。
一般来说我们可以通过下面的步骤来设计一个新的类:
1. 在客户的角度考虑,客户端可能会如何使用该类Think generally about how clients are likely to use the class.)。从进程的一开始,必须记住,库类的设计是为了满足客户的需求而不是为了方便实现者。在专业环境中,确保新的类满足这些需求的最有效方法是让客户参与设计过程。至少在您绘制类设计的轮廓时,您需要将自己置于客户角色中。
2. 确定哪些信息应该是对象的私有成员Determine what information belongs in the private state of each object.)。 虽然私有部分在概念上是实现类的一部分,但是如果你至少知道这个对象应该包含那些信息,这将大大简化后期的设计阶段。在许多情况下,你可以将实例变量写入到私有部分。尽管在这一点上这个精确的细节并不是必要的,但对内部结构的有一定的了解可以使得它更容易定义其构造函数和方法。
3. 定义一组构造函数来创建新对象Define a set of constructors to create new objects)。 类通常定义了多个构造函数的重载形式,从客户端的角度来看,我们通常从需要创建的对象的类型以及客户端在当时的手头信息来思考。通常,每个类都导出一个默认构造函数,这使得客户端可以声明该类的变量并稍后对其进行初始化。在此阶段,考虑构造函数是否需要应用任何限制以确保生成的对象是有效的也是有用的。
4. 罗列将成为该类的公共方法及其操作。在这个阶段,目标是为出口的方法编写原型,从而为你在开始时开发的一般大纲增加了特殊性。你也可以使用此阶段来完善整体设计,遵循我们之前在接口概述的统一性,简单性,充分性,一般性和稳定性原则。(其实就是编写方法的原型)
5. 编码和并测试其实现。(Code and test the implementation) 一旦你有了接口规范,你就需要编写实现它的代码。编写实现当然是必要的工作程序,但也提供验证的设计。在编写实现时,有时需要重新访问接口设计,例如,如果您发现特定功能难以在可接受的效率水平上实现。作为实现者,您还有责任测试类,以确保该类在接口中发布的功能可以实现的。

介绍了基本的步骤,下一篇我们就以有理数类的设计,一步一步体现上面的5个步骤是怎么设计并实现一个类的。

0 0