C++符号重载
·
😉https://share.note.youdao.com/s/dUOYk45u😉【笔记链接】
运算符重载是 C++ 为类类型对象提供的 “语法糖”,本质是具有特殊名字的函数,用于自定义类对象的运算符行为。以下从基础规则、核心场景、特殊运算符重载、关键区别等维度,系统总结运算符重载的核心知识:
一、运算符重载的基本概念与核心规则
1. 本质与命名
- 本质:运算符重载是函数,函数名由
operator+ 运算符构成(如operator==、operator++),包含返回值、参数列表和函数体。 - 调用方式:类对象使用运算符时,编译器自动转换为调用对应重载函数(隐式调用),也可直接显式调用(如
d1.operator==(d2))。
2. 核心规则(必须遵守)
| 规则 | 说明 | 示例 |
|---|---|---|
| 参数个数与运算对象匹配 | 一元运算符(如++)1 个参数,二元运算符(如==)2 个参数;成员函数重载时,this指针占第一个参数,参数数减少 1 个 |
二元运算符==:全局函数需 2 个参数(d1,d2),成员函数仅需 1 个参数(d,this对应d1) |
| 至少 1 个类类型参数 | 禁止仅重载内置类型(如int operator+(int x,int y)),否则编译报错 |
正确:bool operator==(const Date& d1, const Date& d2)(含Date类类型) |
| 不可创建新运算符 | 只能重载 C++ 已有的运算符,不能自定义新符号(如operator@) |
- |
| 5 个运算符不可重载 | 固定为.*、::、sizeof、?:、.,无任何例外 |
- |
| 优先级 / 结合性不变 | 重载后运算符的优先级、结合性与内置类型完全一致(如++优先级高于==) |
- |
二、运算符重载的两种实现方式(成员函数 vs 全局函数)
根据运算符特性,需选择不同的实现方式,核心区别在于this指针的使用。
1. 成员函数重载
- 适用场景:运算符的左侧运算对象是类对象(且需访问类的成员),如
d1 == d2、++d1。 - 核心特点:
- 隐含
this指针,指向左侧运算对象(如d1 == d2中,this对应d1)。 - 二元运算符仅需 1 个参数(右侧运算对象),一元运算符无需参数(
this对应唯一运算对象)。
- 隐含
- 示例(Date 类
==重载):class Date { public: // 成员函数重载==:this对应左侧d1,参数d对应右侧d2 bool operator==(const Date& d) { return _year == d._year && _month == d._month && _day == d._day; } }; // 调用:d1 == d2 → 编译器转换为 d1.operator==(d2)
2. 全局函数重载
- 适用场景:左侧运算对象不是类对象(如
cout << d1中cout是ostream对象),需将非类对象作为第一个参数。 - 核心特点:
- 无
this指针,需显式传入所有运算对象。 - 若需访问类的私有成员,需在类内声明该函数为友元(
friend)。
- 无
- 示例(全局
==重载):class Date { public: int _year, _month, _day; // 若为private,需加友元声明:friend bool operator==(const Date&, const Date&); }; // 全局函数重载==:两个参数分别对应左侧d1、右侧d2 bool operator==(const Date& d1, const Date& d2) { return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day; } // 调用:d1 == d2 → 编译器转换为 operator==(d1, d2)
三、特殊运算符重载:前置++与后置++
++是唯一需要区分 “前置” 和 “后置” 的运算符,C++ 通过参数差异实现重载。
1. 核心区别(语义 + 实现)
| 特性 | 前置++(++d) |
后置++(d++) |
|---|---|---|
| 语义 | 先自增,再使用(返回自增后对象) | 先使用,再自增(返回自增前对象) |
| 函数原型 | Date& operator++()(无参数,返回引用) |
Date operator++(int)(int为占位参数,返回对象) |
| 返回值类型 | 引用(Date&):避免拷贝,支持连续自增(如++(++d)) |
临时对象(Date):返回自增前状态,不支持连续自增 |
| 实现逻辑 | 1. 自增对象成员(如_day++);2. 返回*this |
1. 用临时对象保存当前状态(Date tmp(*this));2. 自增对象成员;3. 返回临时对象 |
2. 代码示例(Date 类)
class Date {
public:
// 前置++:返回引用,支持连续++
Date& operator++() {
cout << "前置++" << endl;
_day++; // 先自增
return *this; // 返回自增后对象
}
// 后置++:int占位参数,返回临时对象
Date operator++(int) {
Date tmp(*this); // 保存自增前状态
cout << "后置++" << endl;
_day++; // 后自增
return tmp; // 返回自增前对象
}
private:
int _year, _month, _day;
};
四、赋值运算符重载(特殊的默认成员函数)
赋值运算符重载是6 个默认成员函数之一,用于 “两个已存在对象间的拷贝”,需与 “拷贝构造” 严格区分。
1. 核心特点
- 必须重载为成员函数:C++ 规定,赋值运算符不能重载为全局函数(否则编译器报错)。
- 参数与返回值:
- 参数:建议
const Date& d(const 保护原对象,引用避免拷贝)。 - 返回值:建议
Date&(引用返回,支持连续赋值如d1 = d2 = d3,返回赋值后左值对象)。
- 参数:建议
- 自赋值检查:必须判断
this != &d(避免自己给自己赋值的无意义操作,若类管理资源还会导致错误)。
2. 代码示例(Date 类)
class Date {
public:
// 赋值运算符重载:支持连续赋值,避免自赋值
Date& operator=(const Date& d) {
if (this != &d) { // 检查自赋值
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this; // 返回赋值后的左值对象(如d1 = d2返回d1)
}
private:
int _year, _month, _day;
};
3. 与拷贝构造的核心区别
| 对比维度 | 赋值运算符重载 | 拷贝构造函数 |
|---|---|---|
| 作用场景 | 两个已存在对象间的拷贝(如d1 = d3,d1、d3 已创建) |
用已有对象初始化新对象(如Date d2(d1)、Date d4 = d1,d2、d4 是新对象) |
| 调用时机 | 对象创建后,执行赋值操作时 | 对象创建时 |
| 函数原型 | Date& operator=(const Date& d) |
Date(const Date& d) |
五、易错点总结
-
成员函数指针调用的括号问题:
调用成员函数指针时,(obj.*pf)()的括号不可省略(.*优先级低于函数调用()),否则会解析为obj.*(pf())(语法错误)。 -
后置
++的临时对象初始化:
后置++必须用Date tmp(*this)初始化临时对象(保存自增前状态),不可直接Date tmp(未初始化,行为未定义)。 -
全局重载的访问权限:
全局运算符重载若需访问类的私有成员,必须在类内声明为友元(如friend bool operator==(const Date&, const Date&))。 -
默认生成的重载函数:
若未显式实现赋值运算符重载 / 拷贝构造,编译器会自动生成默认版本,对内置类型做浅拷贝(仅拷贝指针地址,不拷贝资源)。若类管理资源(如Stack的_a指针),必须手动实现深拷贝,否则会导致 “双重释放” 崩溃。
六、总结:运算符重载的设计原则
- 按需重载:仅重载对类有意义的运算符。
- 遵循语义:重载后的运算符行为需符合用户直觉(如
++要体现 “自增”,==要体现 “相等比较”)。 - 效率优先:参数和返回值优先用
const引用(避免不必要的对象拷贝,如const Date&)。 - 区分场景:左侧是类对象用成员函数重载,左侧是非类对象用全局函数 + 友元(如
operator<<、operator>>)。


更多推荐

所有评论(0)