Как перегрузить постфиксный инкремент

Ответ

Чтобы перегрузить постфиксный инкремент (operator++(int)), нужно объявить метод с фиктивным параметром типа int. Этот параметр не используется, он служит только для отличия сигнатуры от префиксного инкремента (operator++()). Метод должен вернуть копию объекта до увеличения его значения.

#include <iostream>

class Integer {
private:
    int value;
public:
    Integer(int v) : value(v) {}

    // Префиксный инкремент: ++obj
    Integer& operator++() {
        ++value;
        return *this; // Возвращаем ссылку на измененный объект
    }

    // Постфиксный инкремент: obj++
    Integer operator++(int) {
        Integer temp = *this; // Сохраняем текущее состояние
        ++value;              // Инкрементируем внутреннее значение
        return temp;          // Возвращаем старое состояние
    }

    int getValue() const { return value; }
};

int main() {
    Integer a(5);
    Integer b = a++; // b = 5 (старое значение), a становится 6
    Integer c = ++a; // a становится 7, c = 7 (новое значение)

    std::cout << b.getValue() << std::endl; // 5
    std::cout << c.getValue() << std::endl; // 7
    return 0;
}

Ключевые отличия от префиксного инкремента:

  1. Сигнатура: T operator++(int) vs T& operator++().
  2. Семантика: Возвращает значение до изменения.
  3. Эффективность: Постфиксный инкремент обычно менее эффективен из-за необходимости создания и возврата временного объекта. Поэтому в циклах for предпочтительнее использовать префиксную форму (++i).

Ответ 18+ 🔞

А, ну это же классика, ёпта! Сидишь такой, пишешь свой класс, всё красиво, а потом бац — надо перегрузить инкремент. И тут начинается: а какой из них какой, блядь?

Смотри, история простая, но хитрая, как жопа. Есть два инкремента: один делает дело и сразу говорит "я готов" (это префиксный, ++obj), а второй сначала говорит "я какой был", а потом уже делает дело (это постфиксный, obj++). Компилятору-то надо как-то их различать, а имена-то у методов одинаковые — operator++. Вот и придумали гениальный костыль, ядрёна вошь.

Для постфиксного инкремента в сигнатуру метода пихают фиктивный параметр типа int. Просто так, для галочки. Никакого смысла в нём нет, это как соль в кармане — от сглазу. Внутри метода ты должен:

  1. Сохранить текущее состояние объекта (сделать копию) — это будет "старое значение".
  2. Увеличить своё внутреннее поле (то есть сделать то, ради чего всё и затевалось).
  3. Вернуть ту самую сохранённую копию, которая уже устарела, но кому-то нужна.
// Постфиксный инкремент: obj++
Integer operator++(int) {
    Integer temp = *this; // Шаг 1: Сохраняем себя прежнего. "Смотрите, какой я был красавчик!"
    ++value;              // Шаг 2: А теперь реально увеличиваемся. Дело сделано.
    return temp;          // Шаг 3: Возвращаем того, старого. "На, полюбуйся, но это уже не я".
}

А префиксный (++obj) — он без этого дурацкого int, работает напрямую, меняет себя и сразу возвращает ссылку на изменённого себя. Быстро и эффективно.

И вот главный подвох, из-за которого все путаются: постфиксный инкремент обычно менее эффективен, ёб твою мать! Потому что он создаёт лишнюю копию объекта. В примере с int — пофиг, а если объект на 500 байт? Копирование — это овердохуища лишней работы.

Поэтому все опытные ребята в циклах for пишут ++i, а не i++. Разница в скорости может быть, а может и не быть, но принцип-то, блядь, правильный! Зачем создавать временный объект, если можно без него?

Короче, запомни: int в скобках — это волшебный пендель для компилятора, который говорит "эй, это тот инкремент, который возвращает старое значение, и он медленнее, потому что копирует". Всё, больше ни хуя не нужно.