第11章 使用类

探索C++类的更多魔法!学习如何让自定义对象使用`+`、`*`等运算符,并掌握类与基本类型之间灵活转换的技巧。

环节 1:运算符重载 —— 让对象做算术

为什么 `int` 可以相加,而我们自己创建的 `Time` 对象却不行?运算符重载就是给 `+`、`-` 等符号赋予新的能力,让它们也能理解我们的自定义对象。

class Time { private: int hours; int minutes; public: // ... 构造函数 ... // 将 t1 + t2 转换为 t1.operator+(t2) Time operator+(const Time& t) const; }; Time Time::operator+(const Time& t) const { Time sum; sum.minutes = minutes + t.minutes; sum.hours = hours + t.hours + sum.minutes / 60; sum.minutes %= 60; return sum; }

时间计算器 🕰️

输入两个时间,然后点击 `+` 按钮,看看重载后的运算符如何工作。

时间 T1

:
+

时间 T2

:

> 等待计算...

环节 2:友元函数 —— 类的“好帮手”

想让 `cout` 直接打印我们的 `Time` 对象(`cout << t1`),但 `cout` 并不是 `Time` 类的成员,无法访问其私有数据。这时就需要**友元 (friend)**,它像一个被授权的“好帮手”,可以自由进出类的“私有房间”。

自定义对象打印机 🖨️

我们有一个 `Time` 对象,它包含 `2` 小时和 `50` 分钟。点击按钮,使用重载的 `<<` 友元函数将其打印到控制台。

Time myTime(2, 50);

> Console Output:

class Time { // ... 私有数据 ... public: // friend 声明:允许这个外部函数访问私有成员 friend std::ostream& operator<<(std::ostream& os, const Time& t); }; // 全局函数定义,不是成员,没有 Time:: std::ostream& operator<<(std::ostream& os, const Time& t) { // 可以直接访问 t.hours 和 t.minutes os << t.hours << " 小时, " << t.minutes << " 分钟"; return os; // 返回引用以支持链式输出 }

环节 3:类型转换的艺术

C++有时会“偷偷地”帮你转换类型,但这可能导致意外。使用 `explicit` 关键字可以禁止这种“自动翻译”,让代码更安全、更可控。

class Stonewt { // 代表一种英石重量单位 private: double pounds; public: // explicit 禁止了 double 到 Stonewt 的隐式转换 explicit Stonewt(double lbs) { pounds = lbs; } // 转换函数: 允许 Stonewt 显式或隐式地转为 double operator double() const { return pounds; } }; void display(Stonewt st) { /* ... */ } int main() { // display(19.6); // 错误! explicit 禁止隐式转换 display(Stonewt(19.6)); // 正确! 显式转换 Stonewt my_weight(150.9); double lbs = my_weight; // 正确! 调用 operator double() }

严格的类型检查员 👮

`display` 函数只接受 `Stonewt` 类型的参数。尝试将一个 `double` 值传递给它,看看会发生什么。

函数原型: `void display(Stonewt st);`

终点站:编程挑战

练习 1:时间类减法与输出

任务:

基于 `Time` 类(包含小时和分钟),实现减法运算符 `-`,并确保 `Time` 对象可以被 `cout` 正常输出。

预期知识点:

成员函数重载,友元函数重载 `<<`。

点击查看参考答案

File 1: time.h (简化)

#include <iostream> class Time { private: int hours; int minutes; public: Time(int h = 0, int m = 0) : hours(h), minutes(m) {} Time operator-(const Time& t) const; friend std::ostream& operator<<(std::ostream& os, const Time& t); };

File 2: time.cpp (简化)

#include "time.h" Time Time::operator-(const Time& t) const { Time diff; int tot1 = hours * 60 + minutes; int tot2 = t.hours * 60 + t.minutes; int diff_min = tot1 - tot2; diff.hours = diff_min / 60; diff.minutes = diff_min % 60; return diff; } std::ostream& operator<<(std::ostream& os, const Time& t) { os << t.hours << " 小时, " << t.minutes << " 分钟"; return os; }
练习 2:Stonewt 转换

任务:

定义 `Stonewt` 类。提供 `explicit` 构造函数和 `operator double()` 转换函数。测试隐式转换是否被禁用,并测试显式转换。

预期知识点:

`explicit` 关键字,转换函数 `operator typeName()`。

点击查看参考答案

File 1: stonewt.h

class Stonewt { private: double pounds; public: explicit Stonewt(double lbs); operator double() const; operator int() const; };

File 2: stonewt.cpp

#include "stonewt.h" #include <cmath> Stonewt::Stonewt(double lbs) { pounds = lbs; } Stonewt::operator double() const { return pounds; } Stonewt::operator int() const { return static_cast<int>(std::round(pounds)); }

File 3: use_stonewt.cpp

#include "stonewt.h" #include <iostream> int main() { Stonewt my_weight(150.9); // double lbs = my_weight; // OK: 隐式调用 operator double() // display_pounds(my_weight); // 错误: explicit 禁止隐式转换 double d_lbs = (double)my_weight; // OK: 显式转换 std::cout << "显式转换为 double: " << d_lbs << std::endl; int i_lbs = my_weight; // OK: 隐式调用 operator int() std::cout << "隐式转换为 int: " << i_lbs << std::endl; return 0; }