12.8 OVERLOADING INCREMENT AND DECREMENT OPERATORS


12.8 OVERLOADING INCREMENT AND DECREMENT OPERATORS

The goal of this section is to demonstrate the overloading of the increment operator ‘++' and the decrement operator ‘−−', and to also dwell on the interesting problem posed by the fact these operators can be both prefix and postfix.

We will illustrate these with the help of a class Smalllnt that should behave like a regular int as long as the numbers are between 0 and 255, inclusive of both ends of the range. If an attempt is made to alter the value held by a Smalllnt so that it would violate the range constraint, we want an exception to be thrown. We endow the class with a function rangeCheck() to check for range violations:

 
class Smalllnt { int value; // integer value held here class Err {}; // nested class for exceptions int rangeCheck( int i) throw(Err) { if (i < 0 || i > 255) throw Err(); return i; } public: // constructor: Smalllnt( int ival = 0) { try { value = rangeCheck(ival); } catch (Err) { cerr << "Error: Range of Smalllnt violated" << endl; exit(1); } } //.... };

Since we want a Smalllnt to behave very much like a regular integer, it would be nice if we could overload the increment and the decrement operators for Smalllnt. The problem here is how to make a distinction between the overload definitions for the prefix and the postfix versions of the operators. As the following definitions for the increment operator show, this distinction is intimated to the compiler by including the int parameter type in the argument of the postfix version of the overload function, as shown in line (B) below. The compiler passes 0 as the value of the argument when the postfix function is invoked.

 
// overload for the prefix increment operator: inline Smalllnt& Smalllnt::operator++(){ //(A) try { rangeCheck(++value); // increment } catch(Err) { cerr << "Error: Range of Smalllnt violated" << endl; exit(1); } return *this; // fetch } // overload for the postfix increment operator: inline const Smalllnt Smalllnt::operator++(int) { //(B) Smalllnt oldValue = *this; // fetch ++(*this); // increment return oldValue; }

Note from line (B) above that the postfix increment operator returns a const object. This is to prevent[6] a double application of the postfix increment operator, as in

      Smalllnt si (..);      si++++; 

The first application of the increment operator will return a const object that will not allow the second application of the same operator to be invoked on it. A non-const member function cannot be invoked on a const object. (Note that the postfix operator is a non-const member function.) On the other hand, there are no such problems with a double application of the prefix increment operator. The following invocation is legal and will increment si twice:

      Smalllnt si(. .);      ++++si; 

Exactly the same considerations apply to the prefix and the postfix versions of the decrement operator.

The code below shows a working example of SmallInt with overloaded increment and decrement operators. The reason for the use of the explicit modifier for the constructor in line (A), the comment "unsafe implicit conversion" with regard to the statement in line (B), and the "dangers of implicit conversion" comment after the statement in line (C) will be clear from the discussion in the next section.

 
//SmalllntWithIncrDecr . cc #include <iostream> #include <cstdlib> using namespace std; class Smalllnt { int value; class Err {}; int rangeCheck(int i) throw(Err); public: explicit Smalllnt( int ival = 0); //(A) Smalllnt (const Smalllnt& other); Smalllnt& operator=(const Smalllnt& other); Smalllnt& operator=(const int i); //unsafe implicit conversion: operator int() const { return value; } //(B) Smalllnt& operator++(); Smalllnt& operator-(); const Smalllnt operator++(int); const Smalllnt operator--(int); friend ostream& operator<<(ostream& os, const Smalllnt& s); }; //constructor: inline Smalllnt :: Smalllnt (int ival) { try { value = rangeCheck(ival); } catch (Err) { cerr << "Error: Range of SmallInt violated" << endl; exit(1); } } //copy constructor: inline Smalllnt :: SmallInt (const Smalllnt& other) { value = other. value; } //copy assignment operator: inline Smalllnt& Smalllnt ::operator=(const Smalllnt& other){ if (this != &other) value = other. value; return *this; } //assignment from an int: inline SmallInt& Smalllnt::operator=(const int i) { try { value = rangeCheck( i); } catch (Err) { cerr << "Error: Range of SmallInt violated" << endl; exit(1); } return *this; } //prefix increment operator: inline SmallInt& SmallInt::operator++(){ try { rangeCheck( ++value); }catch (Err) { cerr << "Error: Range of Smalllnt violated" << endl; exit(1); } return *this; } //prefix decrement operator: inline Smalllnt& Smalllnt :: operator--() { try { rangeCheck(-- value); } catch (Err) { cerr << "Error: Range of Smalllnt violated" << endl; exit(1); } return *this; } //postfix increment operator: inline const Smalllnt Smalllnt::operator++(int) { Smalllnt oldValue = *this; ++(*this); return oldValue; } //postfix decrement operator: inline const Smalllnt Smalllnt::operator--(int) { Smalllnt oldValue = *this; --(*this); return oldValue; } //range check function: inline int Smalllnt::rangeCheck(int i) throw(Err) { if (i < O || i > 255) throw Err(); return i; } //overload for the output stream operator: ostream& operator<<(ostream& os, const Smalllnt& s) { os << s.value; return os; } int main() { Smalllnt si(3); cout << si + 3.14159 << endl; // 6.14159 cout << ++si << endl; // 4 cout << si++ << endl; // 4 cout << si << endl; // 5 cout << --si << endl; // 4 cout << si << endl; // 4 cout << si--<< endl; // 4 cout << si << endl; // 3 si = 255; cout << si << endl; // si++; // range violated, exception thrown // si = 300; // range violated, exception thrown cout << si + 400 << endl; //(C) // 655 (shows the dangers of // implicit conversion) return 0; }

[6]As Meyers [51, p. 33] has so well explained, a double application of the postfix increment operator does not do what a programmer would like to think it does. So it is best prohibited by a suitable definition of the operator overload function.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net