- 内容提要
- 前言
- 第 1 章 预备知识
- 第 2 章 开始学习 C++
- 第 3 章 处理数据
- 第 4 章 复合类型
- 第 5 章 循环和关系表达式
- 第 6 章 分支语句和逻辑运算符
- 第 7 章 函数——C++的编程模块
- 第 8 章 函数探幽
- 第 9 章 内存模型和名称空间
- 第 10 章 对象和类
- 第 11 章 使用类
- 第 12 章 类和动态内存分配
- 第 13 章 类继承
- 第 14 章 C++中的代码重用
- 第 15 章 友元、异常和其他
- 第 16 章 string 类和标准模板库
- 第 17 章 输入、输出和文件
- 第 18 章 探讨 C++新标准
- 附录 A 计数系统
- 附录 B C++保留字
- 附录 C ASCII 字符集
- 附录 D 运算符优先级
- 附录 E 其他运算符
- 附录 F 模板类 string
- 附录 G 标准模板库方法和函数
- 附录 H 精选读物和网上资源
- 附录 I 转换为 ISO 标准 C++
- 附录 J 复习题答案
11.2 计算时间:一个运算符重载示例
如果今天早上在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论