Navigation



9.7 — Overloading the increment and decrement operators

Overloading the increment (++) and decrement (--) operators are pretty straightforward, with one small exception. There are actually two versions of the increment and decrement operators: a prefix increment and decrement (eg. ++nX; --nY;) and a postfix increment and decrement (eg. nX++; nY--;).

Because the increment and decrement operators modify their operands, they’re best overloaded as member functions. We’ll tackle the prefix versions first because they’re the most straightforward.

Overloading prefix increment and decrement

Prefix increment and decrement is overloaded exactly the same as any normal unary operator. We’ll do this one by example:

class Digit
{
private:
    int m_nDigit;
public:
    Digit(int nDigit=0)
    {
        m_nDigit = nDigit;
    }

    Digit& operator++();
    Digit& operator--();

    int GetDigit() const { return m_nDigit; }
};

Digit& Digit::operator++()
{
    // If our number is already at 9, wrap around to 0
    if (m_nDigit == 9)
        m_nDigit = 0;
    // otherwise just increment to next number
    else
        ++m_nDigit;

    return *this;
}

Digit& Digit::operator--()
{
    // If our number is already at 0, wrap around to 9
    if (m_nDigit == 0)
        m_nDigit = 9;
    // otherwise just decrement to next number
    else
        --m_nDigit;

    return *this;
}

Our Digit class holds a number between 0 and 9. We’ve overloaded increment and decrement so they increment/decrement the digit, wrapping around if the digit is incremented/decremented out range.

Note that we return *this. The overloaded increment and decrement operators return a Digit so multiple operators can be “chained” together. Consequently, we need to return an item of type Digit. Since these operators are implemented as member functions, we can just return *this, which is an item of type Digit!

Overloading postfix increment and decrement

Normally, functions can be overloaded when they have the same name but a different number and/or different type of parameters. However, consider the case of the prefix and postfix increment and decrement operators. Both have the same name (eg. operator++), are unary, and take one parameter of the same type. So how it is possible to differentiate the two when overloading?

The answer is that C++ uses a “dummy variable” or “dummy argument” for the postfix operators. This argument is a fake integer parameter that only serves to distinguish the postfix version of increment/decrement from the prefix version. Here is the above Digit class with both prefix and postfix overloads:

class Digit
{
private:
    int m_nDigit;
public:
    Digit(int nDigit=0)
    {
        m_nDigit = nDigit;
    }

    Digit& operator++(); // prefix
    Digit& operator--(); // prefix

    Digit operator++(int); // postfix
    Digit operator--(int); // postfix

    int GetDigit() const { return m_nDigit; }
};

Digit& Digit::operator++()
{
    // If our number is already at 9, wrap around to 0
    if (m_nDigit == 9)
        m_nDigit = 0;
    // otherwise just increment to next number
    else
        ++m_nDigit;

    return *this;
}

Digit& Digit::operator--()
{
    // If our number is already at 0, wrap around to 9
    if (m_nDigit == 0)
        m_nDigit = 9;
    // otherwise just decrement to next number
    else
        --m_nDigit;

    return *this;
}

Digit Digit::operator++(int)
{
    // Create a temporary variable with our current digit
    Digit cResult(m_nDigit);

    // Use prefix operator to increment this digit
    ++(*this);             // apply operator

    // return temporary result
    return cResult;       // return saved state
}

Digit Digit::operator--(int)
{
    // Create a temporary variable with our current digit
    Digit cResult(m_nDigit);

    // Use prefix operator to increment this digit
    --(*this);             // apply operator

    // return temporary result
    return cResult;       // return saved state
}

int main()
{
    Digit cDigit(5);
    ++cDigit; // calls Digit::operator++();
    cDigit++; // calls Digit::operator++(int);

    return 0;
}

There are a few interesting things going on here. First, note that we’ve distinguished the prefix from the postfix operators by providing an integer dummy parameter on the postfix version. Second, because the dummy parameter is not used in the function implementation, we have not even given it a name. This tells the compiler to treat this variable as a placeholder, which means it won’t warn us that we declared a variable but never used it.

Third, note that the prefix and postfix operators do the same job — they both increment or decrement the class. The difference between the two is in the value they return. The overloaded prefix operators return the class after it has been incremented or decremented. Consequently, overloading these is fairly straightforward. We simply increment or decrement our member variables, and then return *this.

The postfix operators, on the other hand, need to return the state of the class before it is incremented or decremented. This leads to a bit of a conundrum — if we increment or decrement the class, we won’t be able to return the state of the class before it was incremented or decremented. On the other hand, if we return the state of the class before we increment or decrement it, the increment or decrement will never be called.

The typical way this problem is solved is to use a temporary variable that holds the value of the class before it is incremented or decremented. Then the class itself can be incremented or decremented. And finally, the temporary variable is returned to the caller. In this way, the caller receives a copy of the class before it was incremented or decremented, but the class itself is incremented or decremented. Note that this means the return value of the overloaded operator much be a non-reference, because we can’t return a reference to a local variable that will be destroyed when the function exits. Also note that this means the postfix operators are typically less efficient than the prefix operators because of the added overhead of instantiating a temporary variable and returning by value instead of reference.

Finally, note that we’ve written the post-increment and post-decrement in such a way that it calls the pre-increment and pre-decrement to do most of the work. This cuts down on duplicate code, and makes our class easier to modify in the future.

9.8 — Overloading the subscript operator
Index
9.6 — Overloading operators using member functions

31 comments to 9.7 — Overloading the increment and decrement operators

  • Pat

    A few mistakes and a bit of bad style here:

    1. (bad style) Use initialiser syntax for constructors.

       private:
         int value;
       public:
       Digit(int value=0) : value(value)
        {
        }
    

    2. Your postfix operators do not call the prefixed versions.

    Digit& Digit::operator++(int)
    {
        return ++*this; // use prefix version of ++
    }
    Digit& Digit::operator--(int)
    {
        return --*this; // use prefix version of --
    }
    

    3. Postfix and prefix notation differ in their semantics. Hence ++digit is NOT the same as digit++. Observe:

    int i = 5;
    std::cout 
    
    Also note that while the following snippet is perfectly valid C++
    
    int i = 5;
    ++i %= 10; // increment and apply modulo
    

    this snippet is not

    int i = 5;
    i++ %= 10; // generates a compiler error
    

    To fix the digit class, we need to apply another little kind-of-bad-style thing.

    4. Apply const-correctness where appropriate. The GetDigit() method doesn't alter the class' data members and should therefore be marked "const".

    int GetDigit() const { return m_nDigit; }
    

    This takes us back to 3. and allow for the following correction to the semantics of the postfix operators:

    class Digit
    {
    // ...
        const Digit operator++(int); // postfix
        const Digit operator--(int); // postfix
    };
    const Digit Digit::operator++(int)
    {
        Digit result(*this); // store current state
        ++*this;             // apply operator
        return result;       // return saved state
    }
    const Digit Digit::operator--(int)
    {
        Digit result(*this); // store current state
        --*this;             // apply operator
        return result;       // return saved state
    }
    

    The correction also makes it obvious why you should write what you mean, e.g. prefer ++x over x++ if you don't need/want temporaries!

    Regards,
    Pat

  • Pat

    Seems as if a few lines were eaten.
    Paragraph 3. should read:

    int i = 5;
    std::cout < < i++ << '\n';
    i = 5;
    std::cout << ++i << '\n';
    // outputs:
    // 5
    // 6
    

    whereas

    Digit digit(5);
    std::cout < < (digit++).GetDigit() << '\n';
    i = 5;
    std::cout << (++digit).GetDigit() << '\n';
    // outputs:
    // 6
    // 6
    

    which is clearly wrong.

  • Pat, wow, I really blew it! I must have been sleepwalking the day I wrote this section. Thanks for your feedback — I altered the example and rewrote the bottom paragraphs to clarify the wording and address most of the points you mention.

    The only point that I didn’t incorporate is the initializer list, simply because I haven’t covered that concept yet. I figured I’d cover it after I cover composition, which will be the first topic in the next chapter.

  • bao

    In the overloaded postfix operator ++ and –, are you sure you can always return a copy of the temporary local variable by return by value?

    My compiler complains likes this:

    warning: reference to local variable ‘cResult’ returned

    Did you get this warning? How would you avoid it?

    • Looking at the code examples, I don’t see where cResult is returned by reference. cResult is returned by value. You can’t return local values by reference (or address) without problems.

  • Tom

    Alex -

    Somewhere early in this tutorial, preferably at the end of Chapter 0, you need a section on using the debugger and how to step through programs, then encourage your readers to step through the example programs to see how they work.

    Thanks!

    • Using the debugger is in the appendix, sections A.4 and A.5. It’s already written and on the site.

      I didn’t include it as part of the main tutorial because it’s not strictly related to C++, but it definitely is an important thing to know. I will add a note to the end of chapter 0 letting users know about the appendix, because I can see how it might be missed.

  • [...] 2007 Prev/Next Posts « 9.5 — Overloading unary operators +, -, and ! | Home | 9.7 — Overloading the increment and decrement operators » Thursday, October 11th, 2007 at 10:22 [...]

  • Stuart

    Why are the overloaded postfix operator++’s const but the prefix ones not?

    • Stuart

      Also, what’s the difference in putting the const keyword at the start and at the end of the statement in function declarations?

      You covered const member functions previously, where the const keyword is at the end of the statement in the function declaration; but I don’t think you’ve covered what const does at the start of the statement here.

      • Neither the prefix nor postfix operator++ is const — however, the postfix one returns a const Digit.

        When the const is used at the end of a function, it means the entire function is const, and no member variables can be changed. When the const is used at the start of a function, it means the return type of the function is const.

  • D

    Why is the return type of the postfix functions const? When a function returns by value rather than reference, isn’t the return value always constant? So why does it need to be declared as such in this case?

  • Andrian
    # Digit Digit::operator--(int)
    # {
    #     // Create a temporary variable with our current digit
    #     Digit cResult(m_nDigit);
    #
    #     // Use prefix operator to increment this digit
    #     --(*this);             // apply operator
    #
    #     // return temporary result
    #     return cResult;       // return saved state
    # }
    

    The “*this” in this function refers to ‘m_nDigit’?

    • No, this always points to the instance of the class itself, so in this case this points to the Digit object that contains m_nDigit.

      That line simply invokes the prefix decrement operator on the same object that the postfix decrement operator was called on.

  • Israel

    You can replace this:

        // If our number is already at 9, wrap around to 0
        if (m_nDigit == 9)
            m_nDigit = 0;
        // otherwise just increment to next number
        else
            ++m_nDigit;
    

    with:

            m_nDigit = (m_nDigit + 1) % 10;
    
  • Rom

    m_nDigit = (m_nDigit + 1) % 10;

    can be even shorter:

    m_nDigit = ++m_nDigit % 10;

  • Soia

    I have a question.

    The way the postfix is implemented… you can’t do something like this:

     cout << cDigit++ << endl; 

    And I think it’s because the postfix returns a value, and not a reference, whereas the operator<< receives a reference as parameter, and not a value.

    Any thoughts on this? I wanna be able to print a postfix.

    Thanks in advance.

    • Soia

      Turns out it wasn’t the case at all, what happened was that my operator<< recieved the object parameter as not const. It seems that when you pass the object parameter to the operator<< as const, as it should always be, it solves my problem.

  • Mark

    First thanks very much for these tutorials. they’re cracking.

    Just a little proofing moment – in the 2nd to last paragraph “Note that this means the return value of the overloaded operator much be a non-reference” should that be a “must” rather than a “much”?

    Cheers

    Mark

  • mslade

    I was about to note the same error, and my name is also Mark… Coincidence…. or fate?

  • Dr. HaXX

    Second example, line 61: increment should be decrement ;)

  • Aki

    i am not able to use cout << c++ <<

    
    #include <iostream>
    
    using namespace std;
    class Complex
    {
    	int real;
    	int img;
    	public:
    	Complex()
    	{
    		real = 0;
    		img = 0;
    	}
    
    	Complex(int r, int i)
    	{
    		real = r;
    		img = i;
    	}
    
    	Complex& operator++();
    	Complex operator++(int);
    
    	friend Complex operator+(Complex &a, Complex &b);
    	friend ostream& operator<<(ostream &out, Complex &a);
    	friend istream& operator>>(istream &in, Complex &a);
    
    	void display()
    	{
    		using namespace std;
    		cout << real << " + " << img << endl;
    	}
    };
    
    Complex& Complex::operator++()
    {
    	++real;
    	++img;
    	return *this;
    }
    
    Complex Complex::operator++(int)
    {
    	Complex temp(real, img);
    	++(*this);
    	return temp;
    }
    
    Complex operator+(Complex &a, Complex &b)
    {
    	Complex temp;
    	temp.real = a.real + b.real;
    	temp.img = a.img + b.img;
    	return temp;
    }
    ostream& operator<<(ostream &out, Complex &a)
    {
    	using namespace std;
    	out << a.real << " + " << a.img << endl;
    	return out;
    }
    
    istream& operator>>(istream &in, Complex &a)
    {
    	using namespace std;
    	cout << "Enter the real part" << endl;
    	in >> a.real;
    	cout << "Enter the imaginary part" << endl;
    	in >> a.img;
    	return in;
    }
    
    int main()
    {
    	Complex a;
    	cin >> a;
    	Complex b(11,8);
    	cout << "a is :" << a << endl;
    	cout << "b is :" << b << endl;
    	Complex c = a + b;
    	cout << "c is :" << c << endl;
    	cout << c;
    	cout << c++;
    	cout << c;
    	cout << ++c;
    	cout << c;
    }
    
    

    i get the following error:

    complex.cpp: In function ‘int main()’:
    complex.cpp:83:11: error: no match for ‘operator<<’ in ‘std::cout << c.Complex::operator++(0)’

  • MUHAMMAD TASADDUQ ALI

    Hi , and ASSALAM O ALAIKUM

    This is my first comment .
    i am feeling happy to be part of LEARNCPP.COM.

    :)

  • rogererens

    In the discussion at the bottom of the lesson, it seems to me that you use ‘class’ in hand-waving style. I’m not sure, but
    they both increment or decrement the class
    might better read
    they both increment or decrement the class instance
    or even
    they both increment or decrement the class instance member variable
    and
    The overloaded prefix operators return the class
    might be intended to read
    The overloaded prefix operators return the class instance
    In previous lessons, I don’t recall state or classes being returned from functions.

    Hope this helps too :-)

  • niravkarani

    ++cDigit; // calls Digit::operator++();
    cDigit++; // calls Digit::operator++(int);

    How does the compiler know when to call which function?

  • [...] Overloading the increment and decrement operators. ref:http://www.learncpp.com/cpp-tutorial/97-overloading-the-increment-and-decrement-operators/ Q:How to distinguish postfix prefix operator: A:Use dummy function argument A& operator++(); // [...]

  • Invader_Zim

    you have typo, i think …

    in this paragraph
    ” Note that this means the return value of the overloaded operator [[much]] be a non-reference, because we can’t return a reference to a local variable that will be destroyed when the function exits.”

    i think you meant must not much, right ?

You must be logged in to post a comment.