Show Lecture.IncrementDecrementMethods as a slide show.
CS253 Increment Decrement Methods
Why
If you’re writing a class that is numeric or pointery, then it needs:
- Preincrement (
++obj
)
- Postincrement (
obj++
)
- Predecrement (
--obj
)
- Postdecrement (
obj--
)
None of these come for free. You have to write them all.
Preincrement
Preincrement is easy:
// Change the state of the object
// Return the object by reference
Whatever& operator++() { // No argument means preincrement
// Do what is needed to change the object
*this += 1; // just an example
return *this;
}
- Use other methods to do the real work,
if you can—DRY!
- We have to both increment the value and return the modified value.
- The return type is
Whatever &
, a reference to the current object.
- Fast & efficient—no copying is required upon return, since we’re just
returning a reference.
Postincrement
Postincrement is harder, but is the same for nearly all classes:
// Save a copy of the object
// Change the state of the object
// Return the copy by value
Whatever operator++(int) { // Dummy int arg means postincrement
const auto save = *this;
++*this; // Call the preincrement method
return save;
}
- We have to return the old value, before it was incremented.
- Therefore, make a copy, and return that.
- It’s a shame that we have to make a copy, but there’s no alternative.
- Can’t return a reference to the short-lived value on the stack.
Decrement & Guilt
- Pre-/post-decrement is essentially the same.
- Get pre-/post-increment working, and then
(I can’t believe that I’m saying this)
use copy & paste.
Example
class ID {
public:
ID& operator++() {
if (++s[1] > '5') {
s[0]++; s[1] = '1';
}
return *this;
}
string s = "A1";
};
ID ident;
for (int i=0; i<12; i++)
cout << (++ident).s << ' ';
A2 A3 A4 A5 B1 B2 B3 B4 B5 C1 C2 C3
This class creates an ID object, and increments it several times.
Since it’s preincrement, we never see A1
.
That requires postincrement.
Example using postincrement
class ID {
public:
ID& operator++() {
if (++s[1] > '5') {
s[0]++; s[1] = '1';
}
return *this;
}
string s = "A1";
};
ID ident;
for (int i=0; i<12; i++)
cout << (ident++).s << ' '; // 🦡
c.cc:14: error: no ‘operator++(int)’ declared for postfix ‘++’
Oh, yeah—we’ve got to actually write the postincrement method.
Example with postincrement
class ID {
public:
ID& operator++() {
if (++s[1] > '5') {
s[0]++; s[1] = '1';
}
return *this;
}
ID operator++(int) {
const auto save = *this;
++*this; // DRY!
return save;
}
string s = "A1";
};
ID ident;
for (int i=0; i<12; i++)
cout << (ident++).s << ' ';
A1 A2 A3 A4 A5 B1 B2 B3 B4 B5 C1 C2
General Guidelines
- Unless you have a really peculiar class, use the postincrement
boilerplate given above. It’s the same for every class!
- Often,
operator++
should use operator+=
to accomplish
the increment. Less duplication of information—good!
- When you have a choice, use preincrement rather than postincrement,
because postincrement makes a copy, whereas preincrement doesn’t.
- For built-in integers, it doesn’t matter—the compiler will do the
right thing. Follow whatever local coding standards are in force, or
use whichever one looks better to you.