副标题#e#
本文适合低级读者
Chuck Allison 是盐湖城圣 Latter Day 教堂总部下耶稣教堂家属汗青研究处的软件体系设计师。他拥有数学学士和数学硕士学位。他从1975年起开始编程,从1984年起他开始从事c语言的解说和开拓。他今朝的乐趣是面向工具的技能及其教诲。他是X3J16,ANSI C ++尺度化委员会的一员。发送e-mail 到 [email protected],可能拨打电话到 (801)240-4510 均可以与他取得接洽。
在上个月的封装中我提出了一个简朴的C++日期类的雏形。为了提供一个可以或许计较两个日期的隔断的函数,这个类举例说明白C++的下列特征:
内联函数
引用
结构函数
对私有数据成员的会见节制
在这个月的部门里我将增加相关的运算符、输入/输出操纵和获得当前日期的本领。它们示范了下列特征:
运算符重载
流
友元函数
静态成员
当利用日期的时候你常常需要确定某一日期是否在另一日期之前。我将为日期类增加下面这个成员函数(拜见 Listing 1):
int compare(const Date& d2) const;
Date::compare 雷同于strcmp-假如当前工具(*this)在d2之前,它返回一个负整数;假如这两个日期沟通,则返回0;不然返回一个正整数(拜见 Listing 2 中的函数实现和 Listing 3 中的示例措施)。就像你们都很熟悉的C尺度库中的qsort一样,你也可以利用Date::compare来对日期举办排序,就仿佛你利用strcmp对字符串举办排序一样。下面是一个可通报给qsort的较量函数(下个月的代码封装将包罗qsort):#include "date.h"
int datecmp(const void *p1, const void *p2)
{
const Date
*d1p = (const Date *) p1,
*d2p = (const Date *) p2;
return d1p->compare(*d2p);
}
#p#副标题#e#
运算符重载
大大都时候,拥有相关的运算符是更利便的,譬喻:
if (d1 < d2)
// do something appropriate..
利用Date::compare来添加一个"小于"运算符长短常容易的–只要在类的界说里插入下面这个内联成员函数就可以了:
int operator<(const Date& d2) const
{return compare(d2) < 0};
每一个表达式:d1 < d2呈现的处所,城市被编译器翻译成函数挪用的形式:
d1.operator<(d2)
Listing 4 中类的界说中拥有六个相关的操纵符,Listing 5中展示了更新之后的示范措施。 既然函数Date::interval 的成果雷同减法(它给出两个日期的差),把它重定名为Date::operator-就是件很自然的工作了。在做这个工作之前,我们仔细研究一下下列语句的语音:
a = b - c;
无论变量是什么范例,下述语句老是创立的: a 是一个由减法发生的明晰的工具,而且 b – c == – (c – b) 我们利用下列约定俗成的习惯,即一个正的日期工具的所有数据成员都是正的,反之亦然(不答允标记的殽杂)。在 Listing 7 中我用 Date::operator- (const Date&)取代了Date::interval,前者为每一个数据成员增加了正确的标记而且返回从头结构过的类的工具。
Listing 6 中从头界说的类中还包罗了一个一元的"-"运算符函数,它的名字照旧 Date::operator-,可是没有任何参数。编译器将把下列的语句
1 - d2;
-d1;
别离替换为:
d1.operator-(d2); // Calls Date::operator-(const Date&)
d1.operator-(); // Calls Date::operator-()
Listing 8 中有一个利用了新的成员函数的简朴示例措施。
输入输出流
正如我以前所说的一样,一个日期类的工具应该具有和系统内建范例一致的外观和感受–输入/输出支持。C++提供了可以或许处理惩罚尺度范例的的输入输出操纵的流的工具。譬喻下列措施 :
#include <iostream.h>
main()
{
int i;
cout << "Enter an integer: ";
cin >> i;
cout << "You typed " << i << endl;
return 0;
}
的输出功效为:
Enter an integer: 5
You typed 5
cout 是 C++ 流库中提供的 output 流(类ostreom)而cin是C++流库中提供的input流(类istreom),它们别离与尺度输出和尺度输入相关。当编译器看到下面的表达式:
cout << "Enter an integer: "
它将用如下语句取代:
cout.operator<<("Enter an integer: ")
上述语句挪用了成员函数ostream::operator<<(const char *)。同样的,表达式
cout << i
挪用了函数
ostream::-operator<<(int)。
endl 是一个非凡的流指示,它输出一个换行符并清空输出缓冲区。Output行可以连在一起:
cout << "You typed " << i
因为 ostream::operator<< 返回一个到 stream 自身的引用。上述语句酿成了
#p#分页标题#e#
(cout.operator<<("You typed ")).operator<<(i)
为了适应 Date 工具的输出,你需要一个全局函数,该函数将要输出的内容发送给一个给定的输出流,而且返回到谁人流的引用:ostream& operator<<(ostream& os, const Date& d)
{
os << d.get_month() << ''/''
<< d.get_day() << ''/''
<< d.get_year();
return os;
}
这虽然不能是一个成员函数,因为流(并非正在被输出的工具)老是呈此刻流插入标记的左边。
友元
为了提高效率,凡是会赋予 operator<< 进入到一个工具的私有数据成员的权限(大大都的类的实现都提供了相关的I/O操纵符,因此在这种环境下冲破封装的界线好像是较量安详的)。为了穿破对私有数据成员会见的限制,你需要在类的声明中插手如下语句,把operator<< 声明为 Date 的友元:
friend ostream& operator<<(ostream&, const Date&);
Listing 9中展示了新的类的声明,而且包罗了输入函数operator>>的声明。Listing 10中展示了这些函数的实现,Listing 11中有一个简朴的示例措施。
静态成员
C++中的类界说了一个浸染域。这就是为什么函数Date::compare不会和一个叫做compare的全局函数产生斗嘴的原因(纵然它们参数和返回值的范例都沟通)。此刻思量实现文件中的一个数组dtab[]。dtab的静态存储范例使它对文件来说是private的。可是它实际上属于整个类,而不是这个文件。假如我想要通报Date的成员函数到多个文件中,我不得不将需要会见dtab的函数通报到同一个文件中。一个更好的步伐是使dtab成为类的静态成员。静态成员属于整个类,而不是一个单独的工具。这意味着只有一个dtab的拷贝存在,它被所有类的工具所共享。使函数isleap成为static则答允你不需要和一个工具相关就能挪用它,好比,你只需要这样写:isleap(y);
而不需要这样写:
d.isleap(y);
要想使isleap对任何挪用者都可用,使它为 public,用如下方法挪用:
Date::isleap(y);
最后,我将从头界说缺省结构函数,用当前日期初始化类的工具。最后的类的界说、实现和示例措施别离拜见 Listing 12 – Listing 14。
总结
在上面两个部门中,我试图说明C++是如何支持数据抽象–利用者的产品–自界说的数据范例。结构函数使恰当你声明一个工具的时候可以或许自动对它举办初始化。你可以通过声明类的成员为private来掩护它们不受到无意中的会见。重载公用的运算符可以使得你的工具看起来跟系统内建的数据范例很相似–这增加了可读性和可维护性。