C++运算符重载完全指南:从基础到实战应用

目录

一、什么是运算符重载?

[1.1 基本概念](#1.1 基本概念)

[1.2 核心思想](#1.2 核心思想)

二、运算符重载的基本语法

[2.1 成员函数形式](#2.1 成员函数形式)

[2.2 全局函数形式](#2.2 全局函数形式)

[2.3 调用方式](#2.3 调用方式)

三、常用运算符重载详解

[3.1 赋值运算符重载](#3.1 赋值运算符重载)

基本特点

注意事项

[3.2 算术运算符重载](#3.2 算术运算符重载)

日期类示例

[3.3 关系运算符重载](#3.3 关系运算符重载)

完整实现

[3.4 递增递减运算符重载](#3.4 递增递减运算符重载)

前后置区分

[3.5 流运算符重载](#3.5 流运算符重载)

输入输出流

[3.6 下标运算符重载](#3.6 下标运算符重载)

数组类示例

四、特殊运算符和注意事项

[4.1 取地址运算符重载](#4.1 取地址运算符重载)

[4.2 不能重载的运算符](#4.2 不能重载的运算符)

[4.3 类型转换运算符(补充)](#4.3 类型转换运算符(补充))

五、实战:完整的日期类实现

[5.1 头文件(Date.h)](#5.1 头文件(Date.h))

[5.2 实现技巧与最佳实践](#5.2 实现技巧与最佳实践)

六、编译器生成的默认运算符

[6.1 默认赋值运算符](#6.1 默认赋值运算符)

[6.2 何时需要自定义](#6.2 何时需要自定义)

七、总结与建议

[7.1 选择成员函数还是全局函数?](#7.1 选择成员函数还是全局函数?)

[7.2 返回值选择](#7.2 返回值选择)

[7.3 效率考虑](#7.3 效率考虑)

[7.4 一致性原则](#7.4 一致性原则)

八、常见问题与解答

Q1:为什么后置++需要int参数?

Q2:什么时候需要重载赋值运算符?

Q3:运算符重载会降低性能吗?

Q4:可以重载所有运算符吗?


一、什么是运算符重载?

1.1 基本概念

运算符重载是C++中允许我们为自定义类型(类)重新定义运算符行为的强大特性。通过运算符重载,我们可以让自定义类型的对象像内置类型一样使用运算符,使代码更加直观和易读。

1.2 核心思想

  • 目的:增强代码的可读性和简洁性

  • 本质:特殊的成员函数或全局函数

  • 限制

    • 不能创建新的运算符

    • 不能改变运算符的优先级和结合性

    • 至少有一个操作数是自定义类型

二、运算符重载的基本语法

2.1 成员函数形式

cpp

复制代码
class Date {
public:
    Date(int year = 1, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {}
    
    // 成员函数形式的运算符重载
    bool operator==(const Date& d) const {
        return _year == d._year
            && _month == d._month
            && _day == d._day;
    }
    
private:
    int _year;
    int _month;
    int _day;
};

2.2 全局函数形式

cpp

复制代码
class Date {
public:
    // 声明为友元以便访问私有成员
    friend bool operator==(const Date& d1, const Date& d2);
    
private:
    int _year;
    int _month;
    int _day;
};

// 全局函数形式的运算符重载
bool operator==(const Date& d1, const Date& d2) {
    return d1._year == d2._year
        && d1._month == d2._month
        && d1._day == d2._day;
}

2.3 调用方式

cpp

复制代码
int main() {
    Date d1(2024, 7, 5);
    Date d2(2024, 7, 6);
    
    // 隐式调用
    bool result1 = d1 == d2;
    
    // 显式调用
    bool result2 = d1.operator==(d2);  // 成员函数形式
    bool result3 = operator==(d1, d2); // 全局函数形式
    
    return 0;
}

三、常用运算符重载详解

3.1 赋值运算符重载

基本特点
  • 必须重载为成员函数

  • 返回当前对象的引用,支持连续赋值

  • 需要处理自赋值情况

cpp

复制代码
class Date {
public:
    // 赋值运算符重载
    Date& operator=(const Date& d) {
        // 检查自赋值
        if (this != &d) {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        return *this;  // 支持连续赋值
    }
    
private:
    int _year;
    int _month;
    int _day;
};
注意事项
  • 赋值运算符重载与拷贝构造函数的区别:

    • 赋值运算符:两个已存在对象之间的赋值

    • 拷贝构造:用已存在对象创建新对象

cpp

复制代码
int main() {
    Date d1(2024, 7, 5);
    Date d2 = d1;  // 拷贝构造
    Date d3;
    d3 = d1;       // 赋值运算符重载
    
    return 0;
}

3.2 算术运算符重载

日期类示例

cpp

复制代码
class Date {
public:
    // += 运算符重载
    Date& operator+=(int day) {
        if (day < 0) {
            return *this -= -day;
        }
        
        _day += day;
        while (_day > GetMonthDay(_year, _month)) {
            _day -= GetMonthDay(_year, _month);
            ++_month;
            if (_month == 13) {
                ++_year;
                _month = 1;
            }
        }
        return *this;
    }
    
    // + 运算符重载(基于+=实现)
    Date operator+(int day) const {
        Date tmp = *this;
        tmp += day;
        return tmp;
    }
    
    // 日期相减(返回天数差)
    int operator-(const Date& d) const {
        Date max = *this;
        Date min = d;
        int flag = 1;
        
        if (*this < d) {
            max = d;
            min = *this;
            flag = -1;
        }
        
        int n = 0;
        while (min != max) {
            ++min;
            ++n;
        }
        return n * flag;
    }
};

3.3 关系运算符重载

完整实现

cpp

复制代码
class Date {
public:
    // 小于运算符
    bool operator<(const Date& d) const {
        if (_year < d._year) return true;
        if (_year == d._year) {
            if (_month < d._month) return true;
            if (_month == d._month) {
                return _day < d._day;
            }
        }
        return false;
    }
    
    // 基于<实现其他关系运算符
    bool operator<=(const Date& d) const {
        return *this < d || *this == d;
    }
    
    bool operator>(const Date& d) const {
        return !(*this <= d);
    }
    
    bool operator>=(const Date& d) const {
        return !(*this < d);
    }
    
    bool operator==(const Date& d) const {
        return _year == d._year
            && _month == d._month
            && _day == d._day;
    }
    
    bool operator!=(const Date& d) const {
        return !(*this == d);
    }
};

3.4 递增递减运算符重载

前后置区分

cpp

复制代码
class Date {
public:
    // 前置++
    Date& operator++() {
        *this += 1;
        return *this;
    }
    
    // 后置++(int参数仅用于区分,不使用)
    Date operator++(int) {
        Date tmp(*this);
        *this += 1;
        return tmp;
    }
    
    // 前置--
    Date& operator--() {
        *this -= 1;
        return *this;
    }
    
    // 后置--
    Date operator--(int) {
        Date tmp = *this;
        *this -= 1;
        return tmp;
    }
};

3.5 流运算符重载

输入输出流

cpp

复制代码
class Date {
public:
    // 声明友元函数
    friend ostream& operator<<(ostream& out, const Date& d);
    friend istream& operator>>(istream& in, Date& d);
    
private:
    int _year;
    int _month;
    int _day;
};

// 输出流重载
ostream& operator<<(ostream& out, const Date& d) {
    out << d._year << "年" << d._month << "月" << d._day << "日";
    return out;
}

// 输入流重载
istream& operator>>(istream& in, Date& d) {
    cout << "请依次输入年月日:>";
    in >> d._year >> d._month >> d._day;
    
    // 添加验证逻辑
    if (!d.CheckDate()) {
        cout << "日期非法" << endl;
    }
    return in;
}

3.6 下标运算符重载

数组类示例

cpp

复制代码
class MyArray {
public:
    MyArray(int size) : _size(size) {
        _data = new int[size];
    }
    
    ~MyArray() {
        delete[] _data;
    }
    
    // 返回引用以支持 arr[i] = value
    int& operator[](int index) {
        if (index < 0 || index >= _size) {
            throw out_of_range("Index out of range");
        }
        return _data[index];
    }
    
    // const版本,用于const对象
    const int& operator[](int index) const {
        if (index < 0 || index >= _size) {
            throw out_of_range("Index out of range");
        }
        return _data[index];
    }
    
private:
    int* _data;
    int _size;
};

四、特殊运算符和注意事项

4.1 取地址运算符重载

cpp

复制代码
class Date {
public:
    // 普通取地址运算符
    Date* operator&() {
        return this;  // 通常返回this
        // return nullptr;  // 特殊场景:不想让别人获取地址
    }
    
    // const版本
    const Date* operator&() const {
        return this;
    }
};

4.2 不能重载的运算符

以下运算符不能重载:

  1. .(成员访问运算符)

  2. .*(成员指针访问运算符)

  3. ::(作用域解析运算符)

  4. ?:(三目条件运算符)

  5. sizeof(大小运算符)

4.3 类型转换运算符(补充)

cpp

复制代码
class Rational {
public:
    Rational(int num = 0, int den = 1) 
        : numerator(num), denominator(den) {}
    
    // 转换为double类型
    operator double() const {
        return static_cast<double>(numerator) / denominator;
    }
    
private:
    int numerator;
    int denominator;
};

int main() {
    Rational r(3, 4);
    double d = r;  // 调用operator double()
    return 0;
}

五、实战:完整的日期类实现

5.1 头文件(Date.h)

cpp

复制代码
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;

class Date {
    // 友元声明
    friend ostream& operator<<(ostream& out, const Date& d);
    friend istream& operator>>(istream& in, Date& d);
    
public:
    Date(int year = 1900, int month = 1, int day = 1);
    
    // 工具函数
    int GetMonthDay(int year, int month) const;
    bool CheckDate() const;
    
    // 关系运算符
    bool operator<(const Date& d) const;
    bool operator<=(const Date& d) const;
    bool operator>(const Date& d) const;
    bool operator>=(const Date& d) const;
    bool operator==(const Date& d) const;
    bool operator!=(const Date& d) const;
    
    // 算术运算符
    Date& operator+=(int day);
    Date operator+(int day) const;
    Date& operator-=(int day);
    Date operator-(int day) const;
    
    // 日期相减
    int operator-(const Date& d) const;
    
    // 递增递减
    Date& operator++();
    Date operator++(int);
    Date& operator--();
    Date operator--(int);
    
    // 输出
    void Print() const;
    
private:
    int _year;
    int _month;
    int _day;
};

5.2 实现技巧与最佳实践

  1. 基于已实现的运算符实现其他运算符

    cpp

    复制代码
    // 基于<和==实现<=
    bool Date::operator<=(const Date& d) const {
        return *this < d || *this == d;
    }
  2. 复用代码减少重复

    cpp

    复制代码
    // 基于+=实现+
    Date Date::operator+(int day) const {
        Date tmp = *this;
        tmp += day;
        return tmp;
    }
  3. 处理边界情况

    cpp

    复制代码
    // 处理负数天数
    Date& Date::operator+=(int day) {
        if (day < 0) {
            return *this -= -day;
        }
        // ... 正数处理逻辑
    }
  4. const成员函数

    • 不修改成员变量的函数应声明为const

    • 允许const对象调用

六、编译器生成的默认运算符

6.1 默认赋值运算符

如果类没有显式定义赋值运算符,编译器会自动生成一个。自动生成的赋值运算符对内置类型执行逐成员赋值(浅拷贝),对自定义类型调用其赋值运算符。

6.2 何时需要自定义

以下情况需要自定义赋值运算符:

  1. 类管理动态内存(如Stack)

  2. 需要进行深拷贝

  3. 有引用或const成员(通常需要初始化列表)

cpp

复制代码
class Stack {
public:
    // 需要自定义赋值运算符
    Stack& operator=(const Stack& st) {
        if (this != &st) {
            free(_a);
            _a = (int*)malloc(sizeof(int) * st._capacity);
            memcpy(_a, st._a, sizeof(int) * st._top);
            _capacity = st._capacity;
            _top = st._top;
        }
        return *this;
    }
    
private:
    int* _a;
    size_t _capacity;
    size_t _top;
};

七、总结与建议

7.1 选择成员函数还是全局函数?

  • 必须为成员函数=()[]->

  • 建议为成员函数+=-=、前缀++--

  • 建议为全局函数+-<<>>

  • 对称运算符 (如==+)通常应为全局函数

7.2 返回值选择

  • 修改自身的运算符(如+=)返回引用

  • 创建新对象的运算符(如+)返回值

  • 关系运算符返回bool

7.3 效率考虑

  • 避免不必要的拷贝

  • 使用引用参数传递大对象

  • 利用返回值优化(RVO)

7.4 一致性原则

  • 相关运算符应一起重载(如++=

  • 保持运算符的常规语义

  • 提供完整的运算符集合


运算符重载是C++面向对象编程的重要特性,合理使用可以大幅提升代码的简洁性和可读性。掌握运算符重载的原则和技巧,能够帮助你设计出更加优雅和高效的C++类。

相关推荐
2301_795167201 小时前
Python 高手编程系列一十八:子类化内置类型
linux·windows·python
Hello.Reader1 小时前
FF4J 用特性开关玩转 Java 应用灰度与发布
java·开发语言
想唱rap1 小时前
C++之红黑树
开发语言·数据结构·c++·算法
正在走向自律1 小时前
电科金仓 KEMCC-V003R002C001B0001 在CentOS7系统环境内测体验:安装部署与功能实操全记录
数据库·国产数据库·电科金仓·kemcc新版本·kemcc
一 乐1 小时前
数码商城系统|电子|基于SprinBoot+vue的数码商城系统(源码+数据库+文档)
java·前端·javascript·数据库·vue.js·springboot
Dxxyyyy1 小时前
零基础学JAVA--Day40(坦克大战)
java·开发语言
逛逛GitHub1 小时前
盘点 近期优秀的 GitHub 开源项目。
github
郑州光合科技余经理1 小时前
PHP技术栈:上门系统海外版开发与源码解析
java·开发语言·javascript·git·uni-app·php·uniapp
曹牧1 小时前
Oracle:Replace
数据库·oracle