③ 面向对象程序设计 — 第 4 课
a + b = a.operator+(b),右边当参数。cout << 对象 只能写成友元,左边不是对象。ostream& 才能连续 <<;少用 friend。virtual + override。+、==、<< 认识你自己的类——运算符重载;以及给「朋友」开门的 friend。
a + b你希望这样写:
Vector2 c = a + b;
而不是这样:
Vector2 c = a.add(b);
运算符重载就是给 +、==、<< 这些符号定义「对你的类怎么工作」。它不改变运算符的优先级和结合方向,只改变「作用在你的类型上时做什么」。
+class Vector2 {
int x, y;
public:
Vector2(int x, int y) : x(x), y(y) {}
Vector2 operator+(const Vector2& v) const {
return Vector2(x + v.x, y + v.y); // 两个分量分别相加
}
};
int main() {
Vector2 a(1, 2);
Vector2 b(3, 4);
Vector2 c = a + b; // 相当于 a.operator+(b)
cout << c.x << " " << c.y; // 4 6
}
a + b 就是 a.operator+(b)。参数是右边的 b,返回值是新结果。
注意两个细节:参数写成 const Vector2& 是「只读地借用」,不复制;函数末尾的 const 表示 a 自己不被修改——加法本来就不该改左操作数。
==bool operator==(const Vector2& v) const {
return x == v.x && y == v.y;
}
这样就能写 if (a == b),不用再一个个比较分量。
返回 bool 是规矩:比较运算应该给出「是/否」。参数还是 const&,因为它只看不改。
<< —— 配合 cout想让 cout << a 直接打印 (1, 2)。但 cout << a 左边是 cout,不是你的对象,所以不能写成成员函数,要用友元函数:
class Vector2 {
int x, y;
public:
Vector2(int x, int y) : x(x), y(y) {}
friend ostream& operator<<(ostream& out, const Vector2& v);
};
// 友元不是成员函数,但可以访问 private 的 x, y
ostream& operator<<(ostream& out, const Vector2& v) {
out << "(" << v.x << ", " << v.y << ")";
return out; // 返回 out 才能连续 <<
}
两个参数都不能少:第一个是 ostream&(左边那台「输出机器」),第二个是要打印的对象。返回 out 的引用,是为了让 cout << c << endl 这种连续写法成立。
friend 是什么friend 字面意思「朋友」:它允许一个不是成员函数的函数访问你的 private 成员。
| 写法 | 说明 |
|---|---|
friend ostream& operator<<(...) | 声明这个函数是朋友 |
| 普通成员函数 | 本来就是自己人,不用 friend |
cout << 对象 这类「左边不是你的对象」的情况,才需要 friend。
换句话说:friend 是在封装的墙上开一扇小门,只为那些非开不可的写法准备。门开多了,封装就名存实亡。
#include <iostream>
using namespace std;
class Vector2 {
int x, y;
public:
Vector2(int x, int y) : x(x), y(y) {}
Vector2 operator+(const Vector2& v) const {
return Vector2(x + v.x, y + v.y);
}
bool operator==(const Vector2& v) const {
return x == v.x && y == v.y;
}
friend ostream& operator<<(ostream& out, const Vector2& v);
};
ostream& operator<<(ostream& out, const Vector2& v) {
out << "(" << v.x << ", " << v.y << ")";
return out;
}
int main() {
Vector2 a(1, 2), b(3, 4);
Vector2 c = a + b;
cout << c << endl; // (4, 6)
cout << (a == b) << endl; // 0(false)
return 0;
}
读一遍 main:c = a + b 走成员版 operator+;cout << c 走友元版 operator<<;a == b 走成员版 operator==。三个运算符,两条规则:左操作数是你的对象 → 成员;不是 → 友元。
operator<< 写成成员函数:那第一个参数就变成了对象自己,cout << a 匹配不上,编译直接报错。return out;:返回 void 的话,cout << c << endl 就断了链,写不出来。const&:按值传参会多一次拷贝;const 少了又接不住常量对象。&、一边没有,或者 const 对不上,就会变成两个不同的函数,链接时报「找不到定义」。a 是「自己」、b 是参数;减法这类不满足交换律的运算写反了,结果会悄悄出错。cout 这种「左边不是对象」的场景开口子。+ 的成员函数名是什么?cout << 对象 通常要用 friend?a + b 等价于什么?Vector2,成员 int x, yoperator+:两个向量相加operator==:判断两个向量是否相同operator<<:打印成 (x, y)a(1,2) 和 b(3,4),输出 a + b 和 a == boperator*,支持向量乘一个整数(两个分量都放大)。
operator+ 的参数是不是 const Vector2&?函数末尾有没有 const?a + b 的结果里,x 用的是自己的 x 加对方的 v.x 吗?operator<< 是声明成 friend 的自由函数,而不是成员函数?return out;?没有就试试 cout << c << endl;。&、const 是否一字不差?(4, 6) 和 0 吗?不是的话先单独打印 c.x、c.y 定位。返回类型 operator符号(参数)。a + b = a.operator+(b);左操作数是你的对象时,写成成员函数。friend 让非成员函数访问私有成员,只为「左边不是对象」的场景开。cout << 对象 用 friend 重载 operator<<,第一个参数是 ostream&。ostream& 才能连续 <<。vector),把「按下标访问 O(1)、中间插删 O(n)」这件事用一张表钉死,再谈它什么时候不该用。Vector2 到 0401 会换成另一个同样叫 Vector 的东西——std::vector,别搞混。