😉https://share.note.youdao.com/s/dUOYk45u😉【笔记链接】

运算符重载是 C++ 为类类型对象提供的 “语法糖”,本质是具有特殊名字的函数,用于自定义类对象的运算符行为。以下从基础规则、核心场景、特殊运算符重载、关键区别等维度,系统总结运算符重载的核心知识:

一、运算符重载的基本概念与核心规则

1. 本质与命名

  • 本质:运算符重载是函数,函数名由operator + 运算符构成(如operator==operator++),包含返回值、参数列表和函数体。
  • 调用方式:类对象使用运算符时,编译器自动转换为调用对应重载函数(隐式调用),也可直接显式调用(如d1.operator==(d2))。

2. 核心规则(必须遵守)

规则 说明 示例
参数个数与运算对象匹配 一元运算符(如++)1 个参数,二元运算符(如==)2 个参数;成员函数重载时,this指针占第一个参数,参数数减少 1 个 二元运算符==:全局函数需 2 个参数(d1,d2),成员函数仅需 1 个参数(dthis对应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 << d1coutostream对象),需将非类对象作为第一个参数。
  • 核心特点
    • 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)

五、易错点总结

  1. 成员函数指针调用的括号问题
    调用成员函数指针时,(obj.*pf)()的括号不可省略(.*优先级低于函数调用()),否则会解析为obj.*(pf())(语法错误)。

  2. 后置++的临时对象初始化
    后置++必须用Date tmp(*this)初始化临时对象(保存自增前状态),不可直接Date tmp(未初始化,行为未定义)。

  3. 全局重载的访问权限
    全局运算符重载若需访问类的私有成员,必须在类内声明为友元(如friend bool operator==(const Date&, const Date&))。

  4. 默认生成的重载函数
    若未显式实现赋值运算符重载 / 拷贝构造,编译器会自动生成默认版本,对内置类型做浅拷贝(仅拷贝指针地址,不拷贝资源)。若类管理资源(如Stack_a指针),必须手动实现深拷贝,否则会导致 “双重释放” 崩溃。

六、总结:运算符重载的设计原则

  1. 按需重载:仅重载对类有意义的运算符。
  2. 遵循语义:重载后的运算符行为需符合用户直觉(如++要体现 “自增”,==要体现 “相等比较”)。
  3. 效率优先:参数和返回值优先用const引用(避免不必要的对象拷贝,如const Date&)。
  4. 区分场景:左侧是类对象用成员函数重载,左侧是非类对象用全局函数 + 友元(如operator<<operator>>)。

更多推荐