当相处的日子变得漫长,我们或许都会有这样的时刻:看着身边的那个人,脑海里忍不住浮现出 “Ta 要是能变成这样就好了” 的念头。想象一下,此刻你手中正握着一根神奇的魔法棒,只要挥动它,就能把对象变成理想中的样子,你会怎么做呢?
大家好呀,我是灰灰,今天咱们要解锁C++的黑魔法——运算符重载!它能让你自定义的类像int
、string
这些内置类型一样,支持+ - * / <<
等骚操作,从而创作出理想中的对象。准备好你的法杖(键盘),开始施法吧!🧙♂️
一、运算符重载的基本咒语
1.1 重载的本质:给类施法
运算符重载就像给你的类加上超能力,让它们能:
- ✨ 用
+
拼接对象 - ✨ 用
<<
直接打印 - ✨ 用
[]
像数组一样访问 - ✨ 用
->
模拟指针行为
施法原则:
- 不能发明新运算符(比如不能搞个
**
出来) - 至少一个操作数是自定义类型
- 不能改变运算符优先级(
+
永远比*
低级)
二、算术运算符:让对象学会数学
“你可是我的对象,怎么能连相加减都不会?!”
2.1 加法运算符(+
)
咒语效果:实现对象的"合体术"
推荐写法:类外非成员函数(支持对象+数字
和数字+对象
)
class 银行账户 {
public:
银行账户(int 余额 = 0) : 余额(余额) {}
// 友元声明让外部函数能访问私有成员
friend 银行账户 operator+(const 银行账户& 左, const 银行账户& 右);
private:
int 余额;
};
// 类外实现"合体术"
银行账户 operator+(const 银行账户& 左, const 银行账户& 右) {
return 银行账户(左.余额 + 右.余额);
}
// 使用示例
银行账户 我的账户(1000);
银行账户 你的账户(2000);
银行账户 总账户 = 我的账户 + 你的账户; // 余额3000
2.2 复合赋值运算符(+=
)
咒语效果:实现"充值术"
推荐写法:类内成员函数(修改自身)
class 银行账户 {
public:
// 类内施法:this对象会被修改
银行账户& operator+=(int 金额) {
余额 += 金额;
return *this; // 返回引用支持链式调用
}
};
// 使用示例
银行账户 账户(1000);
账户 += 500; // 余额1500
(账户 += 200) += 300; // 余额2000
三、比较运算符:对象间的较量
3.1 相等运算符(==
)
咒语效果:开启"火眼金睛"
推荐写法:类外非成员函数(对称比较)
class 学生档案 {
public:
学生档案(int 学号, string 姓名) : 学号(学号), 姓名(姓名) {}
friend bool operator==(const 学生档案& 左, const 学生档案& 右);
private:
int 学号;
string 姓名;
};
bool operator==(const 学生档案& 左, const 学生档案& 右) {
return 左.学号 == 右.学号 && 左.姓名 == 右.姓名;
}
// 使用示例
学生档案 张三(1001, "张三");
学生档案 李四(1002, "李四");
if(张三 == 李四) { /* 不会执行 */ }
四、自增自减:让对象学会…咳咳咳
4.1 前置++
:瞬间进化
咒语效果:立即强化,返回强化后的自己
写法特点:无参数,返回引用
class 修仙境界 {
public:
修仙境界& operator++() {
修为++;
return *this;
}
private:
int 修为 = 0;
};
// 使用示例
修仙境界 凡人;
++凡人; // 修为+1
4.2 后置++
:影分身进化
对象总是没有时间陪你?拿就让它学会分身吧!
咒语效果:返回进化前的分身,真身悄悄变强
写法特点:带int
占位参数,返回值
class 修仙境界 {
public:
修仙境界 operator++(int) {
修仙境界 分身 = *this; // 保存当前状态
++(*this); // 真身进化
return 分身; // 返回进化前的分身
}
};
// 使用示例
修仙境界 筑基期;
修仙境界 分身 = 筑基期++; // 分身是筑基期,真身变为金丹
五、输入输出:让对象学会哄你
5.1 输出运算符(<<
)
咒语效果:赋予对象"自我展示"能力
强制写法:类外非成员函数(左操作数为ostream
)
class 学生档案 {
public:
friend ostream& operator<<(ostream& 输出流, const 学生档案& 学生);
private:
int 学号;
string 姓名;
};
ostream& operator<<(ostream& 输出流, const 学生档案& 学生) {
输出流 << "学号:" << 学生.学号 << " 姓名:" << 学生.姓名;
return 输出流; // 支持链式调用
}
// 使用示例
学生档案 张三(1001, "张三");
cout << 张三; // 输出:学号:1001 姓名:张三
六、高级咒语:让对象变身容器(海?)
6.1 下标运算符([]
)
咒语效果:让对象像数组一样访问
强制写法:类内成员函数(常提供const和非const版本)
class 魔法书包 {
public:
// 非const版本:可修改
string& operator[](int 索引) {
if(索引<0 || 索引>=size) return... //最好加上安全检查
return 书本列表[索引];
}
// const版本:只读
const string& operator[](int 索引) const {
if(索引<0 || 索引>=size) return...
return 书本列表[索引];
}
private:
vector<string> 书本列表 = {"语文", "数学", "英语"};
int size;
};
// 使用示例
魔法书包 书包;
书包[0] = "魔法语文"; // 修改
cout << 书包[1]; // 读取
6.2 箭头运算符(->
)
咒语效果:让对象像指针一样使用
强制写法:类内成员函数(返回原始指针)
class 智能法杖 {
public:
智能法杖(string 法术名) : 法术(new string(法术名)) {}
string* operator->() const {
return 法术;
}
private:
string* 法术;
};
// 使用示例
智能法杖 法杖("火球术");
cout << 法杖->size(); // 输出3(字符串长度)
七、避坑指南:施法失败的代价
❌ 大坑1:返回局部变量的引用
修仙境界& operator++() {
修仙境界 新境界; // 局部变量
return 新境界; // 返回后对象销毁,引用变幽灵👻
}
✅ 正确姿势:返回*this
的引用
修仙境界& operator++() {
修为++;
return *this; // this指向的对象依然存在
}
❌ 大坑2:忘记处理自赋值
银行账户& operator=(const 银行账户& 其他) {
余额 = 其他.余额; // 如果其他是自己,没问题?
return *this;
}
✅ 正确姿势:检查自赋值
银行账户& operator=(const 银行账户& 其他) {
if(this != &其他) { // 不是自己
余额 = 其他.余额;
}
return *this;
}
八、灵魂总结:运算符重载三定律
- 算术运算符:返回新对象,不改原对象
- 修改型运算符(如
+=
):返回引用,支持链式调用 - 输入输出必须友元,下标箭头必须成员
咒语速记口诀:
加减乘除可友元,赋值下标必成员
前++返引用,后++返分身
输入输出靠友元,箭头指向要小心
魔法的力量固然强大,但无论如何,我们都不应试图去塑造他人,而是要学会欣赏和接纳真实的对方。就像画家欣赏一幅独特的画作,不是因为它没有瑕疵,而是因为它的每一笔都充满故事。生活的美好,在于我们能珍视身边人的独特之处,用理解和包容去浇灌感情之花,这样才能收获真正长久且真挚的情感。