C++中的递增运算符重载

在 C++ 中,递增运算符(++) 分为前置递增(++obj) 和后置递增(obj++),两者语义不同,重载方式也有明显区别。递增运算符是单目运算符,通常重载为类的成员函数(更符合语义,因为操作数是类对象本身),也可重载为全局函数(极少用)。

一、核心区别:前置 vs 后置递增

二、重载语法(成员函数方式,推荐)

  1. 前置递增重载
cpp 复制代码
// 类内声明
类名& operator++();

// 类外定义
类名& 类名::operator++() {
    // 1. 修改对象内部状态(如数值+1)
    // 2. 返回自身引用(*this)
    return *this;
}
  1. 后置递增重载
cpp 复制代码
// 类内声明(int是占位符,无实际意义,仅用于区分前置)
类名 operator++(int);

// 类外定义
类名 类名::operator++(int) {
    // 1. 保存当前对象的副本(临时对象)
    类名 temp = *this;
    // 2. 修改原对象状态
    // 3. 返回副本(原状态)
    return temp;
}

三、基础示例:整数包装类重载 ++

以自定义整数类 MyInt 为例,实现前置 / 后置递增:

cpp 复制代码
#include <iostream>
using namespace std;

class MyInt {
private:
    int val; // 封装的整数值
public:
    // 构造函数
    MyInt(int v = 0) : val(v) {}

    // 1. 重载前置递增(返回引用)
    MyInt& operator++() {
        val++; // 先加1
        return *this; // 返回自身,支持链式操作
    }

    // 2. 重载后置递增(int占位符,返回值)
    MyInt operator++(int) {
        MyInt temp = *this; // 保存当前值
        val++; // 后加1
        return temp; // 返回原状态
    }

    // 辅助:输出重载(方便打印)
    friend ostream& operator<<(ostream& os, const MyInt& mi) {
        os << mi.val;
        return os;
    }
};

int main() {
    MyInt a(5);

    // 前置递增:先加1,再使用
    cout << "++a = " << ++a << endl; // 输出:++a = 6
    cout << "a = " << a << endl;     // 输出:a = 6

    // 后置递增:先使用,再加1
    cout << "a++ = " << a++ << endl; // 输出:a++ = 6
    cout << "a = " << a << endl;     // 输出:a = 7

    // 前置支持链式操作
    MyInt b(10);
    cout << "++(++b) = " << ++(++b) << endl; // 输出:++(++b) = 12

    return 0;
}

四、进阶示例:复数类递增(实部 + 虚部同时 + 1)

cpp 复制代码
#include <iostream>
using namespace std;

class Complex {
private:
    double real; // 实部
    double imag; // 虚部
public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}

    // 前置递增:实部、虚部都+1,返回引用
    Complex& operator++() {
        real++;
        imag++;
        return *this;
    }

    // 后置递增:返回原状态,再修改
    Complex operator++(int) {
        Complex temp = *this;
        real++;
        imag++;
        return temp;
    }

    // 输出重载
    friend ostream& operator<<(ostream& os, const Complex& c) {
        os << real << " + " << imag << "i";
        return os;
    }
};

int main() {
    Complex c(1.5, 2.5);

    cout << "++c = " << ++c << endl;  // 输出:++c = 2.5 + 3.5i
    cout << "c++ = " << c++ << endl;  // 输出:c++ = 2.5 + 3.5i
    cout << "c = " << c << endl;      // 输出:c = 3.5 + 4.5i

    return 0;
}

五、全局函数重载(极少用,了解即可)

若需重载为全局函数(需访问私有成员时声明为友元),语法如下:

cpp 复制代码
#include <iostream>
using namespace std;

class MyInt {
private:
    int val;
public:
    MyInt(int v = 0) : val(v) {}

    // 声明友元:前置递增
    friend MyInt& operator++(MyInt& mi);
    // 声明友元:后置递增
    friend MyInt operator++(MyInt& mi, int);

    friend ostream& operator<<(ostream& os, const MyInt& mi) {
        os << mi.val;
        return os;
    }
};

// 全局定义前置递增
MyInt& operator++(MyInt& mi) {
    mi.val++;
    return mi;
}

// 全局定义后置递增
MyInt operator++(MyInt& mi, int) {
    MyInt temp = mi;
    mi.val++;
    return temp;
}

int main() {
    MyInt a(5);
    cout << ++a << endl; // 6
    cout << a++ << endl; // 6
    cout << a << endl;   // 7
    return 0;
}

六、常见误区与注意事项

1、后置递增的 int 占位符:

这个 int 只是语法标记,无实际参数传递,不能省略(否则编译器无法区分前置 / 后置);

无需给 int 命名(如 operator++(int) 即可,不用写 operator++(int flag))。

2、返回值错误:

前置递增必须返回引用(MyInt&):若返回值,会生成临时对象,链式操作(++(++a))会修改临时对象,而非原对象;

后置递增必须返回值(MyInt):若返回引用,会返回临时对象的引用(临时对象销毁后,引用悬空,导致未定义行为)。

3、修改原对象的语义:

递增运算符的核心是 "修改对象自身",因此优先重载为成员函数(全局函数需传引用,语义不如成员函数直观)。

与 += 配合:若类已有 += 重载,可复用逻辑:

cpp 复制代码
MyInt& operator++() {
    *this += 1; // 复用 += 逻辑
    return *this;
}

七、递减运算符(--)重载

递减运算符(--)的重载逻辑与递增完全一致,仅需将 "+1" 改为 "-1":

cpp 复制代码
// 前置递减
MyInt& operator--() {
    val--;
    return *this;
}

// 后置递减
MyInt operator--(int) {
    MyInt temp = *this;
    val--;
    return temp;
}

总结

递增运算符重载分前置和后置,核心区别是返回值类型和执行顺序;

优先重载为成员函数,前置返回引用(支持链式),后置返回值(带 int 占位符);

遵循 "前置先改后返,后置先返后改" 的语义,符合用户对 ++ 的直觉;

递减运算符重载逻辑与递增完全一致,仅修改数值操作。

相关推荐
九转成圣8 小时前
Java 性能优化实战:如何将海量扁平数据高效转化为类目字典树?
java·开发语言·json
SmartRadio8 小时前
ESP32-S3 双模式切换实现:兼顾手机_路由器连接与WiFi长距离通信
开发语言·网络·智能手机·esp32·长距离wifi
laowangpython8 小时前
Rust 入门:GitHub 热门内存安全编程语言
开发语言·其他·rust·github
我叫汪枫8 小时前
在后台管理系统中,如何递归和选择保留的思路来过滤菜单
开发语言·javascript·node.js·ecmascript
_.Switch8 小时前
东方财富股票数据JS逆向:secids字段和AES加密实战
开发语言·前端·javascript·网络·爬虫·python·ecmascript
软件技术NINI8 小时前
webkit简介及工作流程
开发语言·前端·javascript·udp·ecmascript·webkit·yarn
Brendan_0018 小时前
JavaScript的Stomp.over
开发语言·javascript·ecmascript
念2348 小时前
f5 shape分析
开发语言·javascript·ecmascript
苍穹之跃8 小时前
某量JS逆向
开发语言·javascript·ecmascript
思茂信息8 小时前
CST软件如何进行参数化扫描?
运维·开发语言·javascript·windows·ecmascript·软件工程·软件需求