重载自增(++)与自减(–)运算符总体上较为简单,仅有一处细微差异:这两种运算符实际各有两个版本——前缀自增/自减(例如 ++x; –y;)与后缀自增/自减(例如 x++; y–;)。
由于自增与自减运算符均为单目运算符,并会修改其操作数,因此最宜将其重载为成员函数。我们先处理前缀版本,因为它们最为直观。
重载前缀自增与自减
前缀自增与自减的重载方式与普通单目运算符完全一致。以下通过示例说明:
#include <iostream>
class Digit
{
private:
    int m_digit{};
public:
    Digit(int digit = 0)
        : m_digit{ digit }
    {
    }
    Digit& operator++();
    Digit& operator--();
    friend std::ostream& operator<< (std::ostream& out, const Digit& d);
};
Digit& Digit::operator++()
{
    // 若当前数字已为 9,则回绕至 0
    if (m_digit == 9)
        m_digit = 0;
    // 否则递增到下一数字
    else
        ++m_digit;
    return *this;
}
Digit& Digit::operator--()
{
    // 若当前数字已为 0,则回绕至 9
    if (m_digit == 0)
        m_digit = 9;
    // 否则递减到下一数字
    else
        --m_digit;
    return *this;
}
std::ostream& operator<< (std::ostream& out, const Digit& d)
{
    out << d.m_digit;
    return out;
}
int main()
{
    Digit digit{ 8 };
    std::cout << digit;
    std::cout << ++digit;
    std::cout << ++digit;
    std::cout << --digit;
    std::cout << --digit;
    return 0;
}
Digit 类保存一个介于 0 至 9 的数字。我们重载的自增与自减运算符会在数字越界时回绕。
程序输出
89098
注意我们返回了 *this。重载后的自增与自减运算符返回当前隐式对象,从而使多个运算符可以“链式”调用。
重载后缀自增与自减
通常情况下,函数可因其参数数量或类型不同而进行重载。然而,前缀与后缀自增/自减运算符同名(例如 operator++),均为单目,且参数类型相同。那么重载时如何区分?
C++ 语言规范对此做出特殊规定:编译器检查重载运算符是否带有一个 int 形参。若带 int 形参,则为后缀重载;若无,则为前缀重载。
以下给出同时包含前缀与后缀重载的 Digit 类:
class Digit
{
private:
    int m_digit{};
public:
    Digit(int digit = 0)
        : m_digit{ digit }
    {
    }
    Digit& operator++();   // 前缀无参数
    Digit& operator--();   // 前缀无参数
    Digit operator++(int); // 后缀带 int 参数
    Digit operator--(int); // 后缀带 int 参数
    friend std::ostream& operator<< (std::ostream& out, const Digit& d);
};
// 无参数,故为前缀 operator++
Digit& Digit::operator++()
{
    if (m_digit == 9)
        m_digit = 0;
    else
        ++m_digit;
    return *this;
}
// 无参数,故为前缀 operator--
Digit& Digit::operator--()
{
    if (m_digit == 0)
        m_digit = 9;
    else
        --m_digit;
    return *this;
}
// 带 int 参数,故为后缀 operator++
Digit Digit::operator++(int)
{
    // 创建临时变量保存当前数字
    Digit temp{ *this };
    // 使用前缀运算符递增
    ++(*this); // 应用 operator++
    // 返回临时结果
    return temp; // 返回保存的状态
}
// 带 int 参数,故为后缀 operator--
Digit Digit::operator--(int)
{
    // 创建临时变量保存当前数字
    Digit temp{ *this };
    // 使用前缀运算符递减
    --(*this); // 应用 operator--
    // 返回临时结果
    return temp; // 返回保存的状态
}
std::ostream& operator<< (std::ostream& out, const Digit& d)
{
    out << d.m_digit;
    return out;
}
int main()
{
    Digit digit{ 5 };
    std::cout << digit;
    std::cout << ++digit; // 调用 Digit::operator++();
    std::cout << digit++; // 调用 Digit::operator++(int);
    std::cout << digit;
    std::cout << --digit; // 调用 Digit::operator--();
    std::cout << digit--; // 调用 Digit::operator--(int);
    std::cout << digit;
    return 0;
}
程序输出
5667665
值得注意的几点:
- 通过在后缀版本中添加一个整型哑元参数来区分前缀与后缀运算符。
- 该哑元参数在函数实现中未被使用,因此未命名,这向编译器表明该变量仅为占位符,不会产生“声明但未使用”的警告。
- 前缀与后缀运算符完成的任务相同,均对对象进行自增或自减,区别在于返回值:- 前缀版本返回自增/自减后的对象,因此实现简单:直接修改成员变量后返回 *this。
- 后缀版本需返回操作前的对象状态,于是产生矛盾:若先自增/自减,则无法返回原状态;若先返回原状态,则不会执行自增/自减。
 
- 前缀版本返回自增/自减后的对象,因此实现简单:直接修改成员变量后返回 
常见解决方式是使用临时变量保存操作前的对象值,然后对对象本身进行自增/自减,最后返回该临时变量。如此,调用者得到自增/自减前的副本,而实际对象已完成修改。由于返回的是局部变量,重载运算符的返回类型必须为非引用类型;此外,由于需创建临时变量并以值返回,后缀版本通常比前缀版本效率低。
最后,我们将后缀自增与自减实现为调用前缀版本以完成主要工作,从而减少代码重复并降低未来维护成本。
