Score& operator--() {
--score_;
return(*this);
}
const Score operator--(int x) {
Score tmp(*this);
--(*this);
return(tmp);
}
int getScore() const {return(score_);}
private:
int score_;
};
int main() {
Score player1(50);
player1++;
++player1; // score = 52
cout << 'Счет = ' << player1.getScore() << '
';
(--player1)--; // score_ = 50
cout << 'Счет = ' << player1.getScore() << '
';
}
Операторы инкремента и декремента часто имеют смысл для классов, которые представляют некоторые разновидности целых значений. Если вы понимаете разницу между префиксной и постфиксной формами и следуете соглашениям о возвращаемых значениях, то их легко использовать.
Представьте себе инкремент целого числа. С помощью оператора ++
имеется два способа выполнить его для некоторого целого i
.
i++; // постфиксный
++i; // префиксный
Оба инкрементируют i
: первая версия создает временную копию i
, инкрементирует i
и затем возвращает временное значение, а вторая инкрементирует i
и затем возвращает его. C++ позволяет выполнять перегрузку операторов, что означает, что вы можете заставить свой собственный тип (класс или enum
) вести себя так же, как и int
.
Чтобы добиться нужного эффекта, перегрузите operator++
и operator--
. Пример 8.14 иллюстрирует, как перегружать префиксную и постфиксную версии.
Score& operator++() { // префиксный
++score_;
return(*this);
}
const Score operator++(int) { // постфиксный
Score tmp(*this);
++(*this);
return(tmp);
}
Префикс выглядит так, как и следует ожидать, но компилятор различает эти две версии, и в объявление постфиксной версии включается параметр int
. Он не имеет семантического применения — он всегда передается как ноль, так что его можно игнорировать.
После этого класс Score
можно использовать как int
.
Score player1(50);
player1++;
++player1; // score_ = 52
Вы, вероятно, заметили, что сигнатуры префиксной версии operator++
возвращают ссылку на текущий класс. Именно так и следует делать (а не возвращать, к примеру, void
), чтобы инкрементируемый или декрементируемый объект мог использоваться в других выражениях. Рассмотрим такую строку из примера.
(--player1)--;
Да, это странно, но она иллюстрирует этот момент. Если бы префиксный operator--
не возвращал чего-то осмысленного, то это выражение не скомпилировалось бы. Еще один пример показывает вызов функции.
foo(--player1);
Функция foo
ожидает аргумент типа Score
, и для корректной компиляции именно это должно возвращаться из префиксного operator--
.
Перегрузка операторов — это мощная возможность, которая позволяет для типов, определяемых пользователем, использовать те же операторы, что и для встроенных типов. Сторонники других языков, которые не поддерживают перегрузку операторов, утверждают, что эта возможность сбивает с толку и очень сложна, и, следует признать, может быть перегружено очень много операторов, соответствующих любому поведению. Но когда дело касается простого инкремента и декремента, хорошо иметь возможность изменить поведение класса так, как этого хочется.
Рецепт 8.14.
8.14. Перегрузка арифметических операторов и операторов присвоения для работы с классами
Имеется класс, для которого имеют смысл некоторые из унарных или бинарных операторов С++, и требуется, чтобы пользователи класса могли использовать их при работе с объектами этого класса. Например, если есть класс с именем Balance
, который содержит значение с плавающей точкой (например, баланс счета), будет удобно, если для объектов Balance
можно было бы использовать некоторые стандартные операторы С++, как здесь.
Balance checking(50.0);
savings(100.0);
checking += 12.0;
Balance total = checking + savings;
Перегрузите операторы, которые требуется использовать как методы и отдельные функции, указав аргументы различных типов, для которых данный оператор имеет смысл, как в примере 8.15.
#include <iostream>
using namespace std;
class Balance {
// These have to see private data
friend const Balance operator+(const Balance& lhs, const Balance& rhs);