11.1 运算符重载
11.1.1 什么是运算符重载?
运算符重载允许为自定义类型定义运算符的行为,使代码更直观自然。
没有运算符重载: 有运算符重载:
Time t3 = t1.add(t2); → Time t3 = t1 + t2;
t1.print(); → cout << t1;
运算符重载的本质 :运算符重载是特殊的函数,函数名为 operator运算符。
cpp
// 例如:重载 + 运算符
Time operator+(const Time& t) const;
// 调用方式:t1 + t2 等价于 t1.operator+(t2)
11.1.2 可重载与不可重载的运算符
可以重载的运算符(部分):
| 类别 | 运算符 |
|---|---|
| 算术 | + - * / % |
| 比较 | == != < > <= >= |
| 赋值 | = += -= *= /= |
| 位运算 | & ` |
| 逻辑 | ! && ` |
| 其他 | [] () -> ++ -- << >> |
不能重载的运算符:
::作用域解析.*成员指针访问.成员访问?:条件运算符sizeof
11.1.3 基本运算符重载示例
cpp
// time_class.cpp -- 时间类运算符重载
#include <iostream>
class Time
{
private:
int hours;
int minutes;
public:
Time() : hours(0), minutes(0) {}
Time(int h, int m) : hours(h), minutes(m) {}
void show() const
{
std::cout << hours << "小时 " << minutes << "分钟" << std::endl;
}
// 重载 + 运算符(成员函数)
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;
}
// 重载 - 运算符
Time operator-(const Time& t) const
{
Time diff;
int total1 = hours * 60 + minutes;
int total2 = t.hours * 60 + t.minutes;
int diff_min = total1 - total2;
if (diff_min < 0) diff_min = -diff_min;
diff.hours = diff_min / 60;
diff.minutes = diff_min % 60;
return diff;
}
// 重载 * 运算符(乘以倍数)
Time operator*(double mult) const
{
Time result;
long total_min = (long)(hours * 60 + minutes) * mult;
result.hours = total_min / 60;
result.minutes = total_min % 60;
return result;
}
// 重载 += 运算符
Time& operator+=(const Time& t)
{
minutes += t.minutes;
hours += t.hours + minutes / 60;
minutes %= 60;
return *this;
}
// 重载 == 运算符
bool operator==(const Time& t) const
{
return hours == t.hours && minutes == t.minutes;
}
// 重载 < 运算符
bool operator<(const Time& t) const
{
if (hours != t.hours)
return hours < t.hours;
return minutes < t.minutes;
}
};
int main()
{
using namespace std;
Time t1(2, 35); // 2小时35分
Time t2(1, 40); // 1小时40分
cout << "t1 = "; t1.show();
cout << "t2 = "; t2.show();
Time t3 = t1 + t2; // 调用 operator+
cout << "t1 + t2 = "; t3.show(); // 4小时15分
Time t4 = t1 - t2;
cout << "t1 - t2 = "; t4.show(); // 0小时55分
Time t5 = t1 * 2.5;
cout << "t1 * 2.5 = "; t5.show(); // 6小时27分
t1 += t2;
cout << "t1 += t2 后:"; t1.show();
cout << boolalpha;
cout << "t1 == t2 : " << (t1 == t2) << endl;
cout << "t2 < t3 : " << (t2 < t3) << endl;
return 0;
}
11.2 友元函数
11.2.1 为什么需要友元?
cpp
// 问题:成员函数 operator* 只能处理 Time * double
// 但 double * Time 怎么办?
Time t1(2, 30);
Time t2 = t1 * 2.0; // ✅ t1.operator*(2.0)
// Time t3 = 2.0 * t1; // ❌ 2.0没有operator*成员函数!
解决方案 :使用友元函数(非成员函数,但可以访问私有成员)。
11.2.2 友元函数的定义与使用
cpp
// friend_demo.cpp -- 友元函数示例
#include <iostream>
class Time
{
private:
int hours;
int minutes;
public:
Time(int h = 0, int m = 0) : hours(h), minutes(m) {}
void show() const
{
std::cout << hours << "h " << minutes << "m" << std::endl;
}
// 成员函数版本:Time * double
Time operator*(double mult) const
{
long total = (long)(hours * 60 + minutes) * mult;
return Time(total / 60, total % 60);
}
// 声明友元函数:double * Time
// friend关键字只在类声明中出现
friend Time operator*(double mult, const Time& t);
// 友元:重载 << 运算符(输出流)
friend std::ostream& operator<<(std::ostream& os, const Time& t);
// 友元:重载 >> 运算符(输入流)
friend std::istream& operator>>(std::istream& is, Time& t);
};
// 友元函数定义(不加 friend 关键字,不加 Time::)
Time operator*(double mult, const Time& t)
{
// 可以访问 t 的私有成员
long total = (long)(t.hours * 60 + t.minutes) * mult;
return Time(total / 60, total % 60);
}
// 重载 << 运算符(必须是友元,因为左操作数是ostream)
std::ostream& operator<<(std::ostream& os, const Time& t)
{
os << t.hours << "小时" << t.minutes << "分钟";
return os; // 返回os支持链式输出
}
// 重载 >> 运算符
std::istream& operator>>(std::istream& is, Time& t)
{
is >> t.hours >> t.minutes;
return is;
}
int main()
{
using namespace std;
Time t1(2, 30);
// 成员函数版本
Time t2 = t1 * 2.0;
cout << "t1 * 2.0 = " << t2 << endl; // 使用重载的<<
// 友元函数版本(double在左边)
Time t3 = 3.0 * t1;
cout << "3.0 * t1 = " << t3 << endl;
// 链式输出
cout << "t1=" << t1 << " t2=" << t2 << " t3=" << t3 << endl;
// 输入
Time t4;
cout << "请输入时间(小时 分钟):";
cin >> t4;
cout << "你输入的时间:" << t4 << endl;
return 0;
}
11.2.3 重载 << 和 >> 运算符(详解)
cpp
// io_overload.cpp -- 输入输出运算符重载
#include <iostream>
#include <string>
class Student
{
private:
std::string name;
int age;
double score;
public:
Student(const std::string& n = "", int a = 0, double s = 0.0)
: name(n), age(a), score(s) {}
// 重载 << :必须是友元(ostream在左边)
friend std::ostream& operator<<(std::ostream& os,
const Student& s)
{
os << "姓名:" << s.name
<< " 年龄:" << s.age
<< " 成绩:" << s.score;
return os; // 必须返回os,支持链式:cout << s1 << s2
}
// 重载 >> :必须是友元(istream在左边)
friend std::istream& operator>>(std::istream& is, Student& s)
{
is >> s.name >> s.age >> s.score;
return is; // 必须返回is,支持链式:cin >> s1 >> s2
}
};
int main()
{
using namespace std;
Student s1("张三", 20, 92.5);
Student s2("李四", 21, 88.0);
// 链式输出
cout << s1 << endl;
cout << s2 << endl;
cout << "学生1:" << s1 << "\n学生2:" << s2 << endl;
// 输入
Student s3;
cout << "请输入学生信息(姓名 年龄 成绩):";
cin >> s3;
cout << "输入的学生:" << s3 << endl;
return 0;
}
💡 重载
<<和>>的关键点:
- 必须是友元函数 (因为左操作数是
ostream/istream,不是自定义类)- 必须返回流的引用 (
ostream&/istream&),以支持链式操作- 参数:
operator<<(ostream& os, const 类名& obj)
11.3 重载运算符:成员函数 vs 友元函数
cpp
// member_vs_friend.cpp -- 成员函数与友元函数的选择
#include <iostream>
class Vector2D
{
public:
double x, y;
Vector2D(double x = 0, double y = 0) : x(x), y(y) {}
// ✅ 适合成员函数:左操作数是本类对象
Vector2D operator+(const Vector2D& v) const
{
return Vector2D(x + v.x, y + v.y);
}
Vector2D operator-() const // 一元负号
{
return Vector2D(-x, -y);
}
Vector2D& operator+=(const Vector2D& v)
{
x += v.x; y += v.y;
return *this;
}
// ✅ 适合友元函数:左操作数不是本类(如double * Vector)
friend Vector2D operator*(double scalar, const Vector2D& v)
{
return Vector2D(scalar * v.x, scalar * v.y);
}
// ✅ 必须是友元:左操作数是ostream
friend std::ostream& operator<<(std::ostream& os,
const Vector2D& v)
{
os << "(" << v.x << ", " << v.y << ")";
return os;
}
// 点积(成员函数)
double dot(const Vector2D& v) const
{
return x * v.x + y * v.y;
}
// 模长
double length() const
{
return sqrt(x * x + y * y);
}
};
int main()
{
using namespace std;
Vector2D v1(3.0, 4.0);
Vector2D v2(1.0, 2.0);
cout << "v1 = " << v1 << endl;
cout << "v2 = " << v2 << endl;
cout << "v1 + v2 = " << (v1 + v2) << endl;
cout << "-v1 = " << (-v1) << endl;
cout << "2.0 * v1 = " << (2.0 * v1) << endl;
cout << "v1 · v2 = " << v1.dot(v2) << endl;
cout << "|v1| = " << v1.length() << endl;
v1 += v2;
cout << "v1 += v2 后:" << v1 << endl;
return 0;
}
选择原则:
| 情况 | 推荐方式 |
|---|---|
| 左操作数是本类对象 | 成员函数 |
左操作数不是本类(如 double * 类) |
友元函数 |
<< 和 >> 运算符 |
必须是友元函数 |
= [] () -> 运算符 |
必须是成员函数 |
11.4 重载赋值运算符
cpp
// assignment_overload.cpp -- 赋值运算符重载
#include <iostream>
#include <cstring>
class MyString
{
private:
char* data;
int length;
public:
// 构造函数
MyString(const char* str = "")
{
length = strlen(str);
data = new char[length + 1];
strcpy(data, str);
}
// 拷贝构造函数(深拷贝)
MyString(const MyString& other)
{
length = other.length;
data = new char[length + 1];
strcpy(data, other.data);
std::cout << "拷贝构造:\"" << data << "\"" << std::endl;
}
// 赋值运算符重载(深拷贝)
MyString& operator=(const MyString& other)
{
std::cout << "赋值运算符:\"" << other.data << "\"" << std::endl;
// 1. 防止自赋值(s = s)
if (this == &other)
return *this;
// 2. 释放旧内存
delete[] data;
// 3. 分配新内存并复制
length = other.length;
data = new char[length + 1];
strcpy(data, other.data);
// 4. 返回 *this 支持链式赋值
return *this;
}
// 析构函数
~MyString()
{
delete[] data;
}
void show() const
{
std::cout << "\"" << data << "\" (长度:" << length << ")" << std::endl;
}
friend std::ostream& operator<<(std::ostream& os,
const MyString& s)
{
os << s.data;
return os;
}
};
int main()
{
using namespace std;
MyString s1("Hello");
MyString s2("World");
MyString s3;
cout << "s1 = "; s1.show();
cout << "s2 = "; s2.show();
// 赋值(调用赋值运算符)
s3 = s1;
cout << "s3 = s1 后:"; s3.show();
// 链式赋值
MyString s4, s5;
s4 = s5 = s2; // s5=s2,然后s4=s5
cout << "s4 = "; s4.show();
cout << "s5 = "; s5.show();
// 自赋值(不应崩溃)
s1 = s1;
cout << "自赋值后 s1 = "; s1.show();
return 0;
}
⚠️ 赋值运算符的四个要点:
- 防止自赋值 :
if (this == &other) return *this;- 释放旧资源 :先
delete[]旧内存- 深拷贝:分配新内存并复制内容
- 返回
*this:支持链式赋值a = b = c
11.5 重载下标运算符 \[\]
cpp
// subscript_overload.cpp -- 下标运算符重载
#include <iostream>
#include <stdexcept>
class SafeArray
{
private:
int* data;
int size;
public:
SafeArray(int sz) : size(sz)
{
data = new int[size](); // 初始化为0
}
~SafeArray() { delete[] data; }
// 非const版本:可读可写
int& operator[](int index)
{
if (index < 0 || index >= size)
throw std::out_of_range("索引越界:" + std::to_string(index));
return data[index];
}
// const版本:只读
const int& operator[](int index) const
{
if (index < 0 || index >= size)
throw std::out_of_range("索引越界:" + std::to_string(index));
return data[index];
}
int getSize() const { return size; }
};
int main()
{
using namespace std;
SafeArray arr(5);
// 写入
for (int i = 0; i < 5; i++)
arr[i] = (i + 1) * 10;
// 读取
cout << "数组内容:";
for (int i = 0; i < 5; i++)
cout << arr[i] << " ";
cout << endl;
// 越界检测
try
{
arr[10] = 999; // 抛出异常
}
catch (const out_of_range& e)
{
cout << "捕获异常:" << e.what() << endl;
}
return 0;
}
11.6 重载递增/递减运算符
cpp
// increment_overload.cpp -- 递增递减运算符重载
#include <iostream>
class Counter
{
private:
int value;
public:
Counter(int v = 0) : value(v) {}
// 前缀 ++(先加后用):返回引用
Counter& operator++()
{
++value;
return *this;
}
// 后缀 ++(先用后加):返回值(用int参数区分)
Counter operator++(int)
{
Counter temp = *this; // 保存当前值
++value; // 递增
return temp; // 返回递增前的值
}
// 前缀 --
Counter& operator--()
{
--value;
return *this;
}
// 后缀 --
Counter operator--(int)
{
Counter temp = *this;
--value;
return temp;
}
int getValue() const { return value; }
friend std::ostream& operator<<(std::ostream& os,
const Counter& c)
{
os << c.value;
return os;
}
};
int main()
{
using namespace std;
Counter c(5);
cout << "初始值:" << c << endl;
cout << "前缀++:" << ++c << endl; // 先加:6
cout << "当前值:" << c << endl; // 6
cout << "后缀++:" << c++ << endl; // 先用:6
cout << "当前值:" << c << endl; // 7
cout << "前缀--:" << --c << endl; // 先减:6
cout << "后缀--:" << c-- << endl; // 先用:6
cout << "当前值:" << c << endl; // 5
return 0;
}
💡 前缀 vs 后缀的区分:
- 前缀
++obj:operator++(),无参数,返回引用- 后缀
obj++:operator++(int),有一个int哑参数,返回值(不是引用)
11.7 类型转换运算符
cpp
// conversion_operator.cpp -- 类型转换运算符
#include <iostream>
class Fraction
{
private:
int numerator; // 分子
int denominator; // 分母
public:
Fraction(int n = 0, int d = 1) : numerator(n), denominator(d) {}
// 转换为 double
operator double() const
{
return (double)numerator / denominator;
}
// 转换为 bool(判断是否为零)
explicit operator bool() const
{
return numerator != 0;
}
friend std::ostream& operator<<(std::ostream& os,
const Fraction& f)
{
os << f.numerator << "/" << f.denominator;
return os;
}
};
int main()
{
using namespace std;
Fraction f1(3, 4); // 3/4
Fraction f2(0, 1); // 0/1
cout << "f1 = " << f1 << endl;
// 隐式转换为double
double d = f1;
cout << "f1 转 double = " << d << endl; // 0.75
// 在数学运算中自动转换
cout << "f1 + 1.0 = " << f1 + 1.0 << endl; // 1.75
// explicit转换:必须显式
if (static_cast<bool>(f1))
cout << "f1 非零" << endl;
if (!static_cast<bool>(f2))
cout << "f2 为零" << endl;
return 0;
}
11.8 综合示例:复数类
cpp
// complex_class.cpp -- 综合示例:完整的复数类
#include <iostream>
#include <cmath>
#include <string>
class Complex
{
private:
double real; // 实部
double imag; // 虚部
public:
// 构造函数
Complex(double r = 0.0, double i = 0.0)
: real(r), imag(i) {}
// 访问器
double getReal() const { return real; }
double getImag() const { return imag; }
// 模(绝对值)
double magnitude() const
{
return sqrt(real * real + imag * imag);
}
// 辐角
double argument() const
{
return atan2(imag, real);
}
// 共轭复数
Complex conjugate() const
{
return Complex(real, -imag);
}
// ===== 算术运算符(成员函数)=====
Complex operator+(const Complex& c) const
{
return Complex(real + c.real, imag + c.imag);
}
Complex operator-(const Complex& c) const
{
return Complex(real - c.real, imag - c.imag);
}
Complex operator*(const Complex& c) const
{
return Complex(real * c.real - imag * c.imag,
real * c.imag + imag * c.real);
}
Complex operator/(const Complex& c) const
{
double denom = c.real * c.real + c.imag * c.imag;
return Complex((real * c.real + imag * c.imag) / denom,
(imag * c.real - real * c.imag) / denom);
}
Complex operator-() const // 一元负号
{
return Complex(-real, -imag);
}
// ===== 复合赋值运算符 =====
Complex& operator+=(const Complex& c)
{
real += c.real; imag += c.imag;
return *this;
}
Complex& operator-=(const Complex& c)
{
real -= c.real; imag -= c.imag;
return *this;
}
Complex& operator*=(const Complex& c)
{
*this = *this * c;
return *this;
}
// ===== 比较运算符 =====
bool operator==(const Complex& c) const
{
return real == c.real && imag == c.imag;
}
bool operator!=(const Complex& c) const
{
return !(*this == c);
}
// ===== 类型转换 =====
explicit operator double() const { return magnitude(); }
// ===== 友元函数 =====
// double * Complex
friend Complex operator*(double scalar, const Complex& c)
{
return Complex(scalar * c.real, scalar * c.imag);
}
// 输出运算符
friend std::ostream& operator<<(std::ostream& os,
const Complex& c)
{
os << c.real;
if (c.imag >= 0)
os << "+" << c.imag << "i";
else
os << c.imag << "i";
return os;
}
// 输入运算符
friend std::istream& operator>>(std::istream& is, Complex& c)
{
is >> c.real >> c.imag;
return is;
}
};
int main()
{
using namespace std;
Complex c1(3.0, 4.0); // 3+4i
Complex c2(1.0, -2.0); // 1-2i
cout << "c1 = " << c1 << endl;
cout << "c2 = " << c2 << endl;
cout << "\n===== 算术运算 =====" << endl;
cout << "c1 + c2 = " << (c1 + c2) << endl; // 4+2i
cout << "c1 - c2 = " << (c1 - c2) << endl; // 2+6i
cout << "c1 * c2 = " << (c1 * c2) << endl; // 11-2i
cout << "c1 / c2 = " << (c1 / c2) << endl;
cout << "-c1 = " << (-c1) << endl; // -3-4i
cout << "\n===== 属性 =====" << endl;
cout << "|c1| = " << c1.magnitude() << endl; // 5
cout << "c1的共轭 = " << c1.conjugate() << endl; // 3-4i
cout << "\n===== 标量乘法 =====" << endl;
cout << "2.0 * c1 = " << (2.0 * c1) << endl; // 6+8i
cout << "\n===== 复合赋值 =====" << endl;
Complex c3 = c1;
c3 += c2;
cout << "c1 += c2 后:" << c3 << endl;
cout << "\n===== 比较 =====" << endl;
cout << boolalpha;
cout << "c1 == c2 : " << (c1 == c2) << endl; // false
cout << "c1 != c2 : " << (c1 != c2) << endl; // true
cout << "\n===== 类型转换 =====" << endl;
cout << "double(c1) = " << static_cast<double>(c1) << endl; // 5
return 0;
}
输出:
c1 = 3+4i
c2 = 1-2i
===== 算术运算 =====
c1 + c2 = 4+2i
c1 - c2 = 2+6i
c1 * c2 = 11-2i
c1 / c2 = -1+2i
-c1 = -3-4i
===== 属性 =====
|c1| = 5
c1的共轭 = 3-4i
===== 标量乘法 =====
2.0 * c1 = 6+8i
===== 复合赋值 =====
c1 += c2 后:4+2i
===== 比较 =====
c1 == c2 : false
c1 != c2 : true
===== 类型转换 =====
double(c1) = 5
📝 第11章知识点总结
| 知识点 | 核心要点 |
|---|---|
| 运算符重载 | operator运算符 函数,使自定义类型支持运算符操作 |
| 成员函数重载 | 左操作数是本类对象时使用,this 是左操作数 |
| 友元函数重载 | 左操作数不是本类时使用,可访问私有成员 |
重载 <</>> |
必须是友元函数,必须返回流引用(支持链式操作) |
| 赋值运算符 | 防自赋值 → 释放旧资源 → 深拷贝 → 返回 *this |
下标运算符 [] |
提供 const 和非 const 两个版本,可加边界检查 |
前缀/后缀 ++ |
前缀无参返回引用,后缀有 int 哑参返回值 |
| 类型转换运算符 | operator 目标类型() const,加 explicit 防止隐式转换 |
| 不可重载的运算符 | :: . .* ?: sizeof |
| 必须是成员函数 | = [] () -> 这四个运算符必须重载为成员函数 |