Overloading the Increment and Decrement Operators

Problem

You have a class where the familiar increment and decrement operators make sense, and you want to overload operator++ and operator-- to make incrementing and decrementing objects of your class easy and intuitive to users.

Solution

Overload the prefix and postfix forms of ++ and -- to do what you want. Example 8-14 shows the conventional technique for overloading the increment and decrement operators.

Example 8-14. Overloading increment and decrement

#include using namespace std; class Score { public: Score( ) : score_(0) {} Score(int i) : score_(i) {} Score& operator++( ) { // prefix ++score_; return(*this); } const Score operator++(int) { // postfix Score tmp(*this); ++(*this); // Take advantage of the prefix operator return(tmp); } 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 << "Score = " << player1.getScore( ) << ' '; (--player1)--; // score_ = 50 cout << "Score = " << player1.getScore( ) << ' '; }

 

Discussion

The increment and decrement operators often make sense for classes that represent some sort of integer value. They are easy to use, as long as you understand the difference between prefix and postfix and you follow the conventions for return values.

Think about incrementing an integer. For some integer i, there are two ways to do it with the ++ operator:

i++; // postfix ++i; // prefix

Both increment i: the first version creates a temporary copy of i increments i, then returns the temporary value, the second increments i then returns it. C++ allows operator overloading, which means you can make your favorite user-defined type (a class or an enum) behave like an int in this regard.

Overload operator++ and operator-- to get what you want. Example 8-14 illustrates how to overload both the prefix and postfix versions:

Score& operator++( ) { // prefix ++score_; return(*this); } const Score operator++(int) { // postfix Score tmp(*this); ++(*this); return(tmp); }

Prefix appears as you would expect, but for the compiler to distinguish between the two, an int parameter is included as part of the postfix operator declaration. It has no semantic use; at runtime, it is always passed as zero so you can ignore it.

Once you do this, you can use the Score class as you would an int:

Score player1(50); player1++; ++player1; // score_ = 52

You probably noticed that the signatures for the prefix version of operator++ return a reference to the current class. You should do this (instead of, say, returning void) so that the object that is being incremented or decremented can be used in other expressions. Consider this line from the example:

(--player1)--;

Strange, yes, but it illustrates a point. If prefix operator-- didn't return anything meaningful, then this expression would not compile. Another example would be a function call:

foo(--player1);

The function foo expects an argument of type Score, and that's exactly what you have to return from prefix operator-- for this to compile.

Operator overloading is a powerful feature that lets you use the same operators on user-defined types that you would on built-in types. Proponents of other languages that do not allow operator overloading bemoan the potential for confusion and complexity, and admittedly, lots of operators can be overloaded for any kind of custom behavior. But when it comes to simple increment and decrement, it's nice to be able to customize your classes' behavior to your liking.

See Also

Recipe 8.14

Категории