Chinaunix首页 | 论坛 | 博客
  • 博客访问: 379446
  • 博文数量: 715
  • 博客积分: 40000
  • 博客等级: 大将
  • 技术积分: 5005
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-13 14:46
文章分类

全部博文(715)

文章存档

2011年(1)

2008年(714)

我的朋友

分类:

2008-10-13 16:34:19

Chuck Allison是盐湖城圣Latter Day教堂总部下耶稣教堂家族历史研究处的软件体系设计师。他拥有数学学士和数学硕士学位。他从1975年起开始编程,从1984年起他开始从事c语言的教学和开发。他目前的兴趣是面向对象的技术及其教育。他是X3J16ANSI C ++标准化委员会的一员。,或者拨打电话到(801)240-4510均可以与他取得联系。

 

在上个月的封装中我提出了一个简单的C++日期类的雏形。为了提供一个能够计算两个日期的间隔的函数,这个类举例说明了C++的下列特征:

 

· 内联函数

· 引用

· 构造函数

· 对私有数据成员的访问控制

 

在这个月的部分里我将增加相关的运算符、输入/输出操作和得到当前日期的能力。它们示范了下列特征:

 

·  运算符重载

· 

·  友元函数

·  静态成员

 

当使用日期的时候你经常需要确定某一日期是否在另一日期之前。我将为日期类增加下面这个成员函数(参见 ):

int compare(const Date& d2) const;

Date::compare类似于strcmp如果当前对象(*this)d2之前,它返回一个负整数;如果这两个日期相同,则返回0;否则返回一个正整数(参见 中的函数实现和 中的示例程序)。就像你们都很熟悉的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);
}

运算符重载

大多数时候,拥有相关的运算符是更方便的,例如:

if (d1 < d2)
  // do something appropriate..

使用Date::compare来添加一个“小于”运算符是非常容易的——只要在类的定义里插入下面这个内联成员函数就可以了:

int operator<(const Date& d2) const
{return compare(d2) < 0};
每一个表达式:d1 < d2出现的地方,都会被编译器翻译成函数调用的形式:
d1.operator<(d2)
 
 中类的定义中拥有六个相关的操作符,中展示了更新之后的示范程序。
 
既然函数Date::interval 的功能类似减法(它给出两个日期的差),把它重命名为Date::operator-就是件很自然的事情了。在做这个事情之前,我们仔细研究一下下列语句的语音:
a = b - c;
无论变量是什么类型,下述语句总是成立的:
 
a 是一个由减法产生的明确的对象,并且 b - c == - (c - b) 
 

我们使用下列约定俗成的习惯,即一个正的日期对象的所有数据成员都是正的,反之亦然(不允许符号的混合)。在中我用Date::operator- (const Date&)代替了Date::interval前者为每一个数据成员增加了正确的符号并且返回重新构造过的类的对象。中重新定义的类中还包括了一个一元的“-”运算符函数,它的名字还是Date::operator-,但是没有任何参数。编译器将把下列的语句

d1 - d2;
-d1;

分别替换为:

d1.operator-(d2); // Calls Date::operator-(const Date&)
d1.operator-();   // Calls Date::operator-()

中有一个使用了新的成员函数的简单示例程序。

输入输出流

正如我以前所说的一样,一个日期类的对象应该具有和系统内建类型一致的外观和感觉——输入/输出支持。C++提供了能够处理标准类型的的输入输出操作的流的对象。例如,下列程序

#include 
 
main()
{
  int i;
  cout << "Enter an integer: ";
  cin >> i;
  cout << "You typed " << i << endl;
  return 0;
}
的输出结果为:
Enter an integer: 5
You typed 5
 
  coutC++流库中提供的output流(类ostreom)而cinC++流库中提供的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自身的引用。上述语句变成了
(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&);
中展示了新的类的声明,并且包括了输入函数operator>>的声明。中展示了这些函数的实现,中有一个简单的示例程序。
 
静态成员
C++中的类定义了一个作用域。这就是为什么函数Date::compare不会和一个叫做compare的全局函数发生冲突的原因(即使它们参数和返回值的类型都相同)。现在考虑实现文件中的一个数组dtab[]dtab的静态存储类型使它对文件来说是private的。但是它实际上属于整个类,而不是这个文件。如果我想要传递Date的成员函数到多个文件中,我不得不将需要访问dtab的函数传递到同一个文件中。
一个更好的办法是使dtab成为类的静态成员。静态成员属于整个类,而不是一个单独的对象。这意味着只有一个dtab的拷贝存在,它被所有类的对象所共享。使函数isleap成为static则允许你不需要和一个对象相关就能调用它,比如,你只需要这样写:
isleap(y);
而不需要这样写:
d.isleap(y);
 
要想使isleap对任何调用者都可用,使它为public,用如下方式调用:
Date::isleap(y);
 
最后,我将重新定义缺省构造函数,用当前日期初始化类的对象。最后的类的定义、实现和示例程序分别参见 - 
 
总结
  在上面两个部分中,我试图说明C++是如何支持数据抽象——使用者的产物——自定义的数据类型。构造函数使得当你声明一个对象的时候能够自动对它进行初始化。你可以通过声明类的成员为private来保护它们不受到无意中的访问。重载公用的运算符可以使得你的对象看起来跟系统内建的数据类型很相似——这增加了可读性和可维护性。
 

Listing 1 介绍日期比较函数

// date4.h
 
class Date
{
   int month;
   int day;
   int year;
 
public:
   // Constructors
   Date()
     {month = day = year= 0;}
   Date(int m, int d, int y)
     {month = m; day = d; year = y;}
 
   // Accessor Functions
   int get_month() const
     {return month;}
   int get_day() const
     {return day;}
   int get_year() const
     {return year;}
 
   Date * interval(const Date&) const;
   int compare(const Date&) const;
};
 
// End of File

Listing 2 间隔和比较成员的实现

// date4.cpp
 
#include "date4.h"
 
inline int isleap(int y)
  {return y%4 == 0 && y%100 != 0 || y%400 == 0;}
 
static int dtab[2][13] =
{
  {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
  {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
 
Date * Date::interval(const Date& d2) const
{
   static Date result;
   int months, days, years, prev_month;
 
   // Compute the interval - assume d1 precedes d2
   years = d2.year - year;
   months = d2.month - month;
   days = d2.day - day;
 
   // Do obvious corrections (days before months!)
   //
   // This is a loop in case the previous month is
   // February, and days < -28.
   prev_month = d2.month - 1;
   while (days < 0)
   {
      // Borrow from the previous month
      if (prev_month == 0)
         prev_month = 12;
      -months;
      days += dtab[isleap(d2.year)][prev_month-];
   }
 
   if (months < 0)
   {
      // Borrow from the previous year
      -years;
      months += 12;
   }
 
   // Prepare output
   result.month = months;
   result.day = days;
   result.year = years;
   return &result;
}
 
int Date::compare(const Date& d2) const
{
   int months, days, years, order;
 
   years = year - d2.year;
   months = month - d2.month;
   days = day - d2.day;
 
   // return <0, 0, or >0, like strcmp()
   if (years == 0 && months == 0 && days == 0)
      return 0;
   else if (years == 0 && months == 0)
      return days;
   else if (years == 0)
      return months;
   else
      return years;
}
// End of File

Listing 3 日期比较成员函数的测试

// tdate4.cpp
 
#include 
#include "date4.h"
 
void compare_dates(const Date& d1, const Date& d2)
{
   int compval = d1.compare(d2);
   char *compstr - (compval < 0) ? "precedes" :
     ((compval > 0) ? "follows" : "equals"};
 
   printf("%d/%d/%d %s %d/%d/%d\n",
     d1.get_month(),d1.get_day(0),d1.get_year(),
     compstr,
     d2.get_month(),d2.get_day(),d2.get_year());
}
main()
{
   Date d1(1,1,1970);
   compare dates(d1,Date(10,1,1951));
   compare_dates{d1,Date(1,1,1970));
   compare_dates(d1,Date(12,31,1992));
   return 0;
}
 
/* OUTPUT
 
1/1/1970 follows 10/1/1951
1/1/1970 equals 1/1/1970
1/1/1970 precedes 12/31/1992
*/
 
// End of File

Listing 4 Date class定义相关的运算符

// date5.h
 
class Date
{
   int month;
   int day;
   int year;
 
public:
   // Constructors
   Date()
     {month = day = year = 0;}
   Date(int m, int d, int y)
     {month = m; day = d; year = y;}
 
   // Accessor Functions
   int get_month() const
     {return month;}
   int get_day() const
     {return day;}
   int get_year() const
     {return year;}
 
   Date * interval(const Date&) const;
   int compare(const Date&) const;
 
   // Relational operators
   int operator<(const Date& d2) const
     {return compare(d2) < 0;}
   int operator<=(const Date& d2) const
     {return compare(d2) <= 0;}
   int operator>(const Date& d2) const
     {return compare(d2) > 0;}
   int operator>=(const Date& d2) const
     {return compare(d2) >= 0;}
   int operator!=(const Date& d2) const
     {return compare(d2) != 0;}
   int operator!=(const Date& d2) const
     {return compare(d2) !=0;}
};
 
// End of File

Listing 5 使用日期类里相关的运算符

// tdate5.cpp
 
#include 
#include 
#include "date5.h"
 
void compare_dates(const Date& d1, const Date& d2)
{
   char *compstr = (d1 < d2) ? "precedes" :
     ((d1 > d2) ? "follows" : "equals");
 
   printf("%d/%d/%d %s %d/%d/%d\n",
     d1.get_month(),d1.get_day(),d1.get_year(),
     compstr,
     d2.get_month(),d2.get_day(),d2.get_year());
}
 
main()
{
   Date d1(1,1,1970);
   compare_dates(d1,Date(10,1,1951));
   compare_dates(d1,Date(1,1,1970));
   compare_dates(d1,Date(12,31,1992));
   return 0;
}
 
/* OUTPUT
 
1/1/1970 follows 10/1/1951
1/1/1970 equals 1/1/1970
1/1/1970 precedes 12/31/1992
*/
 
// End of File

Listing 6 Date类添加二元和一元的“-“运算符

// date6.h
 
class Date
{
   int month;
   int day;
   int year;
 
public:
   // Constructors
   Date()
     {month = day = year = 0;}
   Date(int m, int d, int y)
     {month = m; day = d; year = y;}
 
   // Accessor Functions
   int get_month() const
     {return month;}
   int get_day() const
     {return day;}
   int get_year() const
     {return year;}
 
   Date operator-(const Date& d2) const;
   Date& operator-()
     {month = -month; day = -day; year = -year;
      return *this;}
 
   int compare(const Date&) const;
 
   // Relational operators
   int operator<(const Date& d2) const
     {return compare(d2) < 0;}
   int operator<=(const Date& d2) const
     {return compare(d2) <= 0;}
   int operator>(const Date& d2) const
     {return compare(d2) > 0;}
   int operator>=(const Date& d2) const
     {return compare(d2) >= 0;}
   int operator==(const Date& d2) const
     {return compare(d2) == 0;}
   int operator!=(const Date& d2) const
     {return compare(d2) != 0;}
};
 
// End of File

Listing 7 二元“-”运算符的实现

// date6.cpp
#include 
#include "date6.h"
 
inline int isleap(int y)
  {return y%4 == 0 && y%100 != 0 || y%400 == 0;}
 
static int dtab[2][13] =
{
  {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
  {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
 
Date Date::operator-(const Date& d2) const
{
   int months, days, years, prev_month, order;
   const Date * first, * last;
 
   // Must know which date is first
   if (compare(d2) <= 0)
   {
      // this <= d2
      order = -1;
      first = this;
      last = &d2;
   }
   else
   {
      order = 1;
      first = &d2;
      last = this;
   }
 
   // Compute the interval; first <= last
   years = last->year - first->year;
   months = last->month - first->month;
   days = last->day - first->day;
   assert(years >= 0 && months >= 0 && days >= 0);
 
   // Do obvious corrections (days before months!)
   // This is a loop in case the previous month is
   // February, and days < -28.
   prev_month = last->month - 1;
   while (days < 0)
   {
      // Borrow from the previous month
      if (prev_month == 0)
         prev_month = 12;
      --months;
      days += dtab[isleap(last->year)][prey_month--];
   }
 
   if {months < 0)
   {
      // Borrow from the previous year
      --years;
      months += 12;
   }
 
   // Return a date object with the interval
   if (order == -1)
      return Date(-months,-days,-years);
   else
      return Date(months, days, years);
}
 
int Date::compare(const Date& d2) const
{
    // same as in Listing 2
}
// End of File

Listing 8 两个日期相减

// tdate6.cpp:
 
#include 
#include "date6.h"
 
main()
{
   Date d1(1,1,1970), d2(12,8,1992);
   Date result = d1 - d2;
   printf("years: %d, months: %d, days: %d\n",
      result.get_year(),
      result.get_month(),
      result.get_day());
   result = d2 - d1;
   printf("years: %d, months: %d, days: %d\n",
      result.get_year(),
      result.get_month(),
      result.get_day());
   int test = d1 - d2 == -(d2 - d1);
   printf("d1 - d2 == -(d2 - d1)? %s\n",
     test ? "yes" : "no");
   return 0;
}
 
/* OUTPUT
 
years: -22, months: -11, days: -7
years: 22, months: 11, days: 7
d1 - d2 == -(d2 - d1)? yes
*/
 
// End of File

Listing 9 Date类增加输入输出流

// date7.h
 
class ostream;
 
class Date
{
   int month;
   int day;
   int year;
 
public:
   // Constructors
   Date()
     {month = day = year = 0;}
   Date(int m, int d, int y)
     {month = m; day = d; year = y;}
 
   // Accessor Functions
   int get_month() const
     {return month;}
   int get_day() const
     {return day;}
   int get_year() const
     {return year;}
 
   Date operator-(const Date& d2) const;
   Date& operator-()
     {month= -month; day = -day; year = -year;
      return *this;}
 
   int compare(const Date&) const;
 
   // Relational operators
   int operator<(const Date& d2) const
     {return compare(d2) < 0;}
   int operator<=(const Date& d2) const
     {return compare(d2) <= 0;)
   int operator>(const Date& d2) const
     {return compare(d2) > 0;}
   int operator>=(const Date& d2) const
     {return compare(d2) >= 0;}
   int operator==(const Date& d2) const
     {return compare(d2) == 0;}
   int operator!=(const Date& d2) const
     {return compare(d2) != 0)
 
   // I/O operators
   friend ostream& operator<<(ostream&, const Date&);
   friend istream& operator>>(istream&, Date&);
};
 
// End of File

Listing 10 实现Date类的stream I/O函数

#include 
#include "date7.h"
 
ostream& operator<<(ostream& os, const Date& d)
{
   os << d.month << '/' << d.day << '/' << d.year;
   return os;
}
 
istream& operator>>(istream& is, Date& d)
{
   char slash;
   is >> d.month >> slash >> d.day >> slash >> d.year;
   return is;
}
 
// End of File

Listing 11 举例说明Date 类对象的stream I/O

// tdate7.cpp:
 
#include 
#include "date7.h"
 
main()
{
   Date d1, d2;
   cout << "Enter a date: ";
   cin >> d1;
   cout << "Enter another date: ";
   cin >> d2;
   cout << "d1 - d2 = "<< d1 - d2 << endl;
   cout << "d2 - d1 = "<< d2 - d1 << endl;
   return 0;
}
 
/* OUTPUT
 
Enter a date: 10/1/1951
Enter another date: 5/1/1954
d1 - d2 = -7/0/-2
d2 - d1 = 7/0/2
*/
 
// End of File
 

Listing 12 定义静态成员

// date8.h
 
// Forward declarations
class istream;
class ostream;
 
class Date
{
   int month;
   int day;
   int year;
 
   static int dtab[2][13];
 
public:
   // Constructors
   Date();         // Get today's date (see .cpp file)
   Date(int m, int d, int y)
     {month = m; day = d; year = y;}
 
   // Accessor Functions
   int get_month() const
     {return month;}
   int get_day() const
     {return day;}
   int get_year() const
     {return year;}
 
   Date operator-(const Date& d2) const;
   Date& operator-()
     {month = -month; day = -day; year = -year;
      return *this;}
 
   int compare(const Date&) const;
 
   // Relational operators
   int operator<(const Date& d2) const
     {return compare{d2) < 0;}
   int operator<=(const Date& d2) const
     {return compare(d2) <= 0;}
   int operator>(const Date& d2) const
     {return compare(d2) > 0;}
   int operator>=(const Date& d2) const
     {return compare(d2) >= 0;}
   int operator==(const Date& d2) const
     {return compare(d2) == 0;}
   int operator!=(const Date& d2) const
     {return compare(d2) != 0;}
 
   // Stream I/O operators
   friend ostream& operator<<(ostream&, const Date&);
   friend istream& operator>>(istream&, Date&);
 
   static int isleap(int y)
     {return y%4 == 0 && y%100 != 0 || y%400 == 0;}
};
 
// End of File
 

Listing 13 Date类的最终实现

// date8.cpp
 
#include 
#include 
#include 
#include "date8.h"
 
// Must initialize statics outside the class definition
int Date::dtab[2][13] =
{
  {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
  {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
 
Date Date::operator-(const Date& d2) const
{
   int months, days, years, prev_month, order;
   const Date * first, * last;
 
   // Must know which date is first
   if (compare(d2) <= 0)
   {
      // this <= d2
      order= -1;
      first = this;
      last = &d2;
   }
   else
   {
      order = 1;
      first = &d2;
      last = this;
   }
 
   // Compute the interval; first <= last
   years = last->year - first->year;
   months = last->month - first->month;
   days = last->day - first->day;
   assert(years >= 0 && months >= 0 && days >= 0);
 
   // Do obvious corrections (days before months!)
   //
   // This is a loop in case the previous month is
   // February, and days < -28.
   prev_month = last->month - 1;
   while (days < 0)
   {
      // Borrow from the previous month
      if (prev_month == 0)
         prev_month = 12;
      -months;
      days += dtab[isleap(last->year)][prev_month-];
   }
 
   if (months < 0)
   {
      // Borrow from the previous year
      -years;
      months += 12;
   }
 
   // Return a date object with the interval
   if (order == 1)
      return Date(-months,-days,-years);
   else
      return Date(months,days,years);
}
 
int Date::compare(const Date& d2) const
{
   int months, days, years, order;
 
   years = year - d2.year;
   months = month - d2.month;
   days = day - d2.day;
 
   // return <0, 0, or >0, like strcmp()
   if (years == 0 && months == 0 && days == 0)
      return 0;
   else if (years == 0 && months == 0)
      return days;
   else if (years == 0)
      return months;
   else
      return years;
}
 
ostream& operator<<(ostream& os, const Date& d)
{
   os << d.month << '/' << d.day << '/' << d.year;
   return os;
}
 
istream& operator>>(istream& is, Date& d)
{
   char slash;
   is >> d.month >> slash >> d.day >> slash >> d.year;
   return is;
}
 
Date::Date()
(
   // Get today's date
   time_t tval = time(0);
   struct tm *tmp= localtime(&tval);
 
   month = tmp->tm_mon+1;
   day = tmp->tm_mday;
   year = tmp->tm_year + 1900;
}
// End of File
 

Listing 14 得到今天的日期

// tdate8.cpp:
 
#include 
#include "date8.h"
 
main()
{
   Date today, d2;
   cout << "Today's date is "<< today << endl;
   cout << "Enter another date: ";
   cin >> d2;
   cout << "today - d2 = "<< today - d2 << endl;
   cout << "d2 - today = "<< d2 - today << endl;
   return 0;
}
 
/* OUTPUT
Today's date is 12/12/1992
Enter another date: 1/1/1970
today - d2 = 11/11/22
d2 - today = -11/-11/-22
*/
// End of File

 

 

--------------------next---------------------

阅读(203) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~