Borland Online And The Cobb Group Present:


March, 1994 - Vol. 1 No. 3

C++ Language tip - Increment- and decrement-operator overloading

This article is based on a submission from Eric Nagler.

In the November/December issue of Inside Turbo C++, we described how you can define new versions of the pre-increment and post-increment operators. Unfortunately, we fell victim to two common mistakes: returning integer values from the functions instead of class objects or object references and not implementing true post-increment operator behavior.

In this article, we'll show you how to override the pre- and post-increment operators. We'll present and discuss a class that implements these operators, and demonstrate correct pre- and post-increment operation with a sample program.

Background

If you've been using Turbo C++ or Borland C++ for a long time, you may have noticed that current implementations of the language (Borland C++ 3.0 and above) allow you to overload the post-increment and post-decrement operators for your classes. However, this hasn't always been the case.

Before AT&T released C++ version 2.1, you could overload only the pre-increment and the pre-decrement operators. If you applied the post-increment or the postdecrement operator to a user-defined object, the compiler would instead call the pre-increment or the pre-decrement function.

Once AT&T released version 2.1, you could overload the post-increment and post-decrement operators as well. However, many programmers mistakenly believe that the compiler will take care of implementing the postfix behavior for them (incrementing or decrementing the data value after evaluating the expression). In fact, you're responsible for reproducing this functionality.

Before we overload these operators for a class, let's review the behavior of the prefix and postfix increment operators for built-in types by writing a simple program. Then we'll substitute a user-defined type for the built-in type in our program and confirm that it still behaves correctly.

Pre- and Post-increment behavior for Built-in types

Launch the Borland C++ Integrated Development Environment (IDE) and choose New from the File menu. When a new editor window appears, enter the program from Listing A. (If you're using Borland C++ 4.0, create a new DOS-target project named PREPOST.IDE and then enter this data in PREPOST.CPP.)


Listing A: PREPOST.CPP

#include <iostream.h>

int main()
{
  int x = 1;
  int y = 1;

  cout << "++x = " << ++x << "\n";
  cout << "x = " << x << "\n\n";

  cout << "y++ = " << y++ << "\n";
  cout << "y = " << y << "\n";

  return 0;
}

When you compile and run the program PREPOST.CPP (with Borland C++ 4.0, you'll have to run the program from an MS-DOS Prompt), you'll see the following output:

++x = 2
x = 2

y++ = 1
y = 2

As you can see, the processor increments the variable x before sending the value of x to the output stream cout. However, the processor sends the value of y to the output stream before incrementing the variable.

Overloading the Pre-increment Operator for a class

Implementing the prefix increment or decrement operator is fairly simple. Your main concern when creating the prefix operators is to decide what incrementing means for an object of this class. In most cases, there's a specific data member in the class that you'll want to increment or decrement.

After you pick the member to increment, you'll write the pre-increment operator function in the following form:

LowClass& operator++()
{
    ++dataMember;
    return *this
}

Here, LowClass is the name of the class and dataMember is the name of the member you want to increment.

The return type of the operator++() function is always a reference to the object itself, which we've shown here by returning the dereferenced this pointer. Writing the function this way is important because it allows you to use the prefix operator for objects of this class in the same way you use it for built-in types (for example, chained operations or complex expressions).

Overloading the Post-Increment operator for a class

Unfortunately, implementing the postfix increment or decrement operator is a little more complex than the prefix versions. Here, you'll shift your attention away from the meaning of incrementing an object of a class and focus on duplicating the postfix operator behavior.

Most of the time, you'll write the post-increment operator function in the following form:

LowClass operator++(int)
{
    LowClass oldCopy(*this);
    ++(*this);
    return oldCopy;
}

Once again, LowClass is the name of the class and dataMember is the name of the member you want to increment.

However, you'll notice the return type isn't a reference in this case but an actual LowClass object named oldCopy, which we've created inside the function. The oldCopy object is a duplicate of the object before the increment operation. Therefore, a complex expression that applies the post-increment operator will see the object as it was­­not as it exists after it's been incremented.

For consistency, you'll also notice that we call the pre-increment operator to increment the object's value. If the process of incrementing an object of this class becomes more complex, we'll have to change the code only in the pre-increment operator function, and we'll guarantee that both operators behave the same way.

Now, let's create a real class that implements the pre- and post-increment operators. Since the pre- and post-decrement operators work in a similar fashion (but with the opposite effect), we'll show only the increment operators.

An Example

To apply these functions to a real class, return to the Borland C++ IDE and reopen the PREPOST.CPP file. Move the cursor below the line

#include <iostream.h>

and then enter the code from Listing B.


LISTING B: Integer class definition demonstrating pre- and post-increment operators

class Integer
{
  public:
    Integer(int newValue = 0) :
      intValue(newValue) {}

    Integer& operator++()
      {
        cout << "Pre-increment\n";
        ++intValue;
        return *this;
      }

    Integer operator++(int)
      {
        cout << "Post-increment\n";
        Integer temp(*this);
        ++(*this);
        return temp;
      }

    operator int() const
      { return intValue; }

  private:
    int intValue;
};

You'll notice that we've added the conversion function

operator int() const { return intValue; }

to this class. This function will allow the compiler to convert an Integer object to an int value. (The const specifier in the function's signature prevents us from modifying the object during the conversion.)

When you finish entering the code from Listing B, find the lines

int x = 1;
int y = 1;

and change them to read

Integer x = 1;
Integer y = 1;

Now, we'll be able to see if an Integer object (which is really just a class-hidden int value) behaves identically to a regular int value.

Once again, compile and run the PREPOST.CPP file. You should see the following output:

Pre-increment
++x = 2
x = 2
Post-increment Pre-increment y++ = 1 y = 2

As you can see, when we call the pre-increment operator for the Integer class, it increments the object's value before the compiler passes the object to the console output stream cout. When we call the post-increment operator, it passes a copy of the old Integer object to the stream and then increments the object's value by calling the pre-increment operator.

Conclusion

By overloading the prefix and postfix versions of the increment and decrement operators for your classes, you can make objects of those classes mimic the behavior of the built-in types. In addition, by implementing these operators correctly, you'll allow yourself and other programmers to use objects of these classes with a simpler, more intuitive syntax.

Eric Nagler is a professional instructor of C and C++ in the San Jose, California, area. He is the author of Learning C++: A Hands-on Approach, published by West Publishing Company.

Return to the Borland C++ Developer's Journal index

Subscribe to the Borland C++ Developer's Journal


Copyright (c) 1996 The Cobb Group, a division of Ziff-Davis Publishing Company. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis Publishing Company is prohibited. The Cobb Group and The Cobb Group logo are trademarks of Ziff-Davis Publishing Company.