返回介绍

11.2 计算时间:一个运算符重载示例

发布于 2024-10-08 23:14:08 字数 7519 浏览 0 评论 0 收藏 0

如果今天早上在 Priggs 的账户上花费了 2 小时 35 分钟,下午又花费了 2 小时 40 分钟,则总共花了多少时间呢?这个示例与加法概念很吻合,但要相加的单位(小时与分钟的混合)与内置类型不匹配。第 7 章通过定义一个 travel_time 结构和将这种结构相加的 sum( ) 函数来处理类似的情况。现在将其推广,采用一个使用方法来处理加法的 Time 类。首先使用一个名为 Sum( ) 的常规方法,然后介绍如何将其转换为重载运算符。程序清单 11.1 列出了这个类的声明。

程序清单 11.1 mytime0.h

Time 类提供了用于调整和重新设置时间、显示时间、将两个时间相加的方法。程序清单 11.2 列出了方法定义。请注意,当总的分钟数超过 59 时,AddMin( ) 和 Sum( ) 方法是如何使用整数除法和求模运算符来调整 minutes 和 hours 值的。另外,由于这里只使用了 iostream 的 cout,且只使用了一次,因此使用 std::cout 比导入整个名称空间更经济。

程序清单 11.2 mytime0.cpp

来看一下 Sum( ) 函数的代码。注意参数是引用,但返回类型却不是引用。将参数声明为引用的目的是为了提高效率。如果按值传递 Time 对象,代码的功能将相同,但传递引用,速度将更快,使用的内存将更少。

然而,返回值不能是引用。因为函数将创建一个新的 Time 对象(sum),来表示另外两个 Time 对象的和。返回对象(如代码所做的那样)将创建对象的副本,而调用函数可以使用它。然而,如果返回类型为 Time &,则引用的将是 sum 对象。但由于 sum 对象是局部变量,在函数结束时将被删除,因此引用将指向一个不存在的对象。使用返回类型 Time 意味着程序将在删除 sum 之前构造它的拷贝,调用函数将得到该拷贝。

警告:

不要返回指向局部变量或临时对象的引用。函数执行完毕后,局部变量和临时对象将消失,引用将指向不存在的数据。

最后,程序清单 11.3 对 Time 类中计算时间总和的部分进行了测试。

程序清单 11.3 usetime0.cpp

下面是程序清单 11.1、程序清单 11.2 和程序清单 11.3 组成的程序的输出:

11.2.1 添加加法运算符

将 Time 类转换为重载的加法运算符很容易,只要将 Sum( ) 的名称改为 operator +( ) 即可。这样做是对的,只要把运算符(这里为+)放到 operator 的后面,并将结果用作方法名即可。在这里,可以在标识符中使用字母、数字或下划线之外的其他字符。程序清单 11.4 和程序清单 11.5 反映了这些细微的修改。

程序清单 11.4 mytime1.h

程序清单 11.5 mytime1.cpp

和 Sum( ) 一样,operator +( ) 也是由 Time 对象调用的,它将第二个 Time 对象作为参数,并返回一个 Time 对象。因此,可以像调用 Sum( ) 那样来调用 operator +( ) 方法:

但将该方法命令为 operator +( ) 后,也可以使用运算符表示法:

这两种表示法都将调用 operator +( ) 方法。注意,在运算符表示法中,运算符左侧的对象(这里为 coding)是调用对象,运算符右边的对象(这里为 fixing)是作为参数被传递的对象。程序清单 11.6 说明了这一点。

程序清单 11.6 usetime1.cpp

下面是程序清单 11.4~程序清单 11.6 组成的程序的输出:

总之,operator +( ) 函数的名称使得可以使用函数表示法或运算符表示法来调用它。编译器将根据操作数的类型来确定如何做:

可以将两个以上的对象相加吗?例如,如果 t1、t2、t3 和 t4 都是 Time 对象,可以这样做吗:

为回答这个问题,来看一些上述语句将被如何转换为函数调用。由于+是从左向右结合的运算符,因此上述语句首先被转换成下面这样:

然后,函数参数本身被转换成一个函数调用,结果如下:

上述语句合法吗?是的。函数调用 t2.operator+(t3) 返回一个 Time 对象,后者是 t2 和 t3 的和。然而,该对象成为函数调用 t1.operator+( ) 的参数,该调用返回 t1 与表示 t2 和 t3 之和的 Time 对象的和。总之,最后的返回值为 t1、t2 和 t3 之和,这正是我们期望的。

11.2.2 重载限制

多数 C++运算符(参见表 11.1)都可以用这样的方式重载。重载的运算符(有些例外情况)不必是成员函数,但必须至少有一个操作数是用户定义的类型。下面详细介绍 C++对用户定义的运算符重载的限制。

1.重载后的运算符必须至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载运算符。因此,不能将减法运算符(−)重载为计算两个 double 值的和,而不是它们的差。虽然这种限制将对创造性有所影响,但可以确保程序正常运行。

2.使用运算符时不能违反运算符原来的句法规则。例如,不能将求模运算符(%)重载成使用一个操作数:

同样,不能修改运算符的优先级。因此,如果将加号运算符重载成将两个类相加,则新的运算符与原来的加号具有相同的优先级。

3.不能创建新运算符。例如,不能定义 operator **( ) 函数来表示求幂。

4.不能重载下面的运算符。

  • sizeof:sizeof 运算符。
  • .:成员运算符。
  • . *:成员指针运算符。
  • :::作用域解析运算符。
  • ?::条件运算符。
  • typeid:一个 RTTI 运算符。
  • const_cast:强制类型转换运算符。
  • dynamic_cast:强制类型转换运算符。
  • reinterpret_cast:强制类型转换运算符。
  • static_cast:强制类型转换运算符。

然而,表 11.1 中所有的运算符都可以被重载。

5.表 11.1 中的大多数运算符都可以通过成员或非成员函数进行重载,但下面的运算符只能通过成员函数进行重载。

  • =:赋值运算符。
  • ( ):函数调用运算符。
  • [ ]:下标运算符。
  • ->:通过指针访问类成员的运算符。

注意:

本章不介绍这里列出的所有运算符,但附录 E 对本书正文中没有介绍的运算符进行了总结。

表 11.1 可重载的运算符

+

-

*

/

%

^

&

~=

!

=

<

>

+=

-=

*=

/=

%=

^=

&=

|=

<<

>>

>>=

<<=

==

!=

<=

>=

&&

++

−−

,

−>*

−>

()

[]

new

delete

new []

delete []

除了这些正式限制之外,还应在重载运算符时遵循一些明智的限制。例如,不要将*运算符重载成交换两个 Time 对象的数据成员。表示法中没有任何内容可以表明运算符完成的工作,因此最好定义一个其名称具有说明性的类方法,如 Swap( )。

11.2.3 其他重载运算符

还有一些其他的操作对 Time 类来说是有意义的。例如,可能要将两个时间相减或将时间乘以一个因子,这需要重载减法和乘法运算符。这和重载加法运算符采用的技术相同,即创建 operator –( ) 和 operator *( ) 方法。也就是说,将下面的原型添加到类声明中:

程序清单 11.7 是新的头文件。

程序清单 11.7 mytime2.h

然后将新增方法的定义添加到实现文件中,如程序清单 11.8 所示。

程序清单 11.8 mytime2.cpp

完成上述修改后,就可以使用程序清单 11.9 中的代码来测试新定义了。

程序清单 11.9 usetime2.cpp

下面是程序清单 11.7~程序清单 11.9 组成的程序得到的输出:

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文