C/C++ 入门:运算符重载与静态成员

类的成员不只是数据和函数:还能让自定义类型的对象像 int 一样用 +==<<,也能让某些成员属于”类”而不是某个对象。

📚 基本概念速读

名称 定义 省流
运算符重载(operator overloading) 给自定义类型定义运算符的行为 对象也能加减比较
operator+ 重载 + 运算符的成员函数 定义加法
operator<< 重载输出运算符 对象直接 cout
operator== 重载相等比较 定义”相等”
静态成员变量 属于类而不是某个对象的变量 全类共享一份
静态成员函数 不依赖具体对象、用类名调用的函数 类级操作

🧩 为什么需要运算符重载

没有运算符重载时,比较两个自定义对象要写函数调用:

1
2
3
4
5
Point a{1, 2};
Point b{3, 4};

// Point c = a + b; // 不支持:+ 不知道 Point 怎么加
Point c = add(a, b); // 只能这样

运算符重载让自定义类型用起来像内置类型,代码更直观:

1
2
3
Point c = a + b;              // 直观
if (a == b) { ... } // 直接比较
cout << a << endl; // 直接输出

➕ 运算符重载基础

重载运算符的成员函数写法:operator 后跟运算符符号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
using namespace std;

class Point {
private:
int x, y;

public:
Point(int px, int py) : x(px), y(py) {}

// 重载 +:返回两个点坐标相加的新点
Point operator+(const Point& other) const
{
return Point(x + other.x, y + other.y);
}

// 重载 ==:坐标都相等才算相等
bool operator==(const Point& other) const
{
return x == other.x && y == other.y;
}

int getX() const { return x; }
int getY() const { return y; }
};

int main()
{
Point a(1, 2);
Point b(3, 4);

Point c = a + b; // 调用 a.operator+(b)
cout << c.getX() << " " << c.getY() << endl; // 4 6

if (a == b) {
cout << "相等" << endl;
} else {
cout << "不相等" << endl;
}
return 0;
}

关键点:

  1. operator+ 是成员函数,a + b 等价于调用 a.operator+(b),左操作数就是 this
  2. 返回新对象而不是修改自身;
  3. const 成员函数保证”比较/加法不修改对象”。

🖨️ 重载输出运算符 <<

<< 不能作为成员函数重载(左操作数是 cout,不是我们的类),要写成非成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
using namespace std;

class Point {
private:
int x, y;

public:
Point(int px, int py) : x(px), y(py) {}

int getX() const { return x; }
int getY() const { return y; }
};

// 非成员函数:左操作数是 ostream&,右操作数是 Point
ostream& operator<<(ostream& out, const Point& p)
{
out << "(" << p.getX() << ", " << p.getY() << ")";
return out; // 返回 out 才能连续输出
}

int main()
{
Point p(1, 2);
cout << p << endl; // (1, 2)
return 0;
}

返回 ostream& 是为了支持链式输出:cout << p << " " << q

🧮 重载下标运算符 []

容器类常用,比如让自定义类能像数组一样按下标访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class IntArray {
private:
int data[10];

public:
int& operator[](int index)
{
return data[index]; // 返回引用,才能赋值
}
};

IntArray arr;
arr[3] = 42; // 调用 operator[]
cout << arr[3] << endl; // 42

🧱 静态成员变量

静态成员变量属于类,所有对象共享同一份,而不是每个对象各有一份。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
using namespace std;

class Counter {
private:
static int count; // 声明:属于类的变量

public:
Counter()
{
count++; // 每创建一个对象,计数加一
}

static int getCount() // 静态成员函数
{
return count;
}
};

// 类外定义并初始化:静态成员变量必须在这里给一次定义
int Counter::count = 0;

int main()
{
Counter a;
Counter b;
Counter c;

cout << Counter::getCount() << endl; // 3,用类名调用
return 0;
}

要点:

特性 普通成员变量 静态成员变量
归属 每个对象一份 整个类共享一份
访问 通过对象 通过类名 类名::变量 或对象
定义 类内自动创建 需要在类外定义一次

⚙️ 静态成员函数

静态成员函数不依赖具体对象,用类名直接调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MathUtils {
public:
static int square(int x)
{
return x * x;
}
};

int main()
{
int r = MathUtils::square(5); // 不需要创建对象
return 0;
}

静态成员函数的限制:没有 this 指针,所以不能访问普通成员变量(不知道操作哪个对象的),只能访问静态成员和其他静态函数。

特性 普通成员函数 静态成员函数
依赖对象 是,有 this 否,没有 this
调用方式 对象调用 类名调用
能访问普通成员 不能
能访问静态成员

⚠️ 常见误区

误区 正解
运算符重载是给内置类型用的 内置类型运算符是固定的,重载只针对自定义类型
<< 重载必须是成员函数 << 左操作数是流对象,要用非成员函数形式
重载 + 应该修改左操作数 通常返回新对象,a + b 不应改变 a
静态成员变量每个对象一份 全类共享一份,且在类外定义一次
静态成员函数能访问普通成员变量 静态函数没有 this,只能访问静态成员
重载运算符可以随便改语义 应保持直觉语义(+ 是加法、== 是相等),别滥用

✅ 总结

运算符重载让自定义类型用起来像内置类型(+==<<[]),静态成员让数据或操作属于类本身;两者的共同点是让类的表达力更强。

这一章补上了类与对象(第 7 章)之后的两块常用拼图。接下来无论是写容器类、工具类,还是做计数统计,都有了对应的语法。

Happy Hacking! 🎉