SOLUTIONS ■ 409 inline void go_on() { cout << "\n\nGo on with return! "; cin.sync(); cin.clear(); // No previous input while( cin.get() != '\n') ; } int menu(); // Enter a command char askForSave(); // Prompt user to save. char header[] = "\n\n * * * * * Telephone List * * * * *\n\n"; TelList myFriends; // A telephone list int main() { int action = 0; // Command string name; // Read a name while( action != 'Q') { action = menu(); cls(); cout << header << endl; switch( action) { // // case 'S': case 'F': case 'A': case 'D': // unchanged (refer to the solutions of chapter 16). // case 'O': // To open a file if(myFriends.isDirty() && askForSave() == 'y') myFriends.save(); if( myFriends.load()) cout << "Telephone list read from file " << myFriends.getFilename() <<"!" << endl; else cerr << "Telephone list not read!" << endl; go_on(); break; case 'U': // Save as if( myFriends.saveAs()) cout << "Telephone list has been saved in file: " << myFriends.getFilename() << " !" <<endl; else cerr << "Telephone list not saved!" << endl; go_on(); break; 410 ■ CHAPTER 18 FUNDAMENTALS OF FILE INPUT AND OUTPUT case 'W': // Save if( myFriends.save()) cout << "Telephone list has been saved in " << "the file " << myFriends.getFilename() << endl; else cerr << "Telephone list not saved!" << endl; go_on(); break; case 'Q': // Quit if( myFriends.isDirty() && askForSave() == 'Y') myFriends.save(); cls(); break; } } // End of while return 0; } int menu() { static char menuStr[] = // . . . "\n " "\n O = Open a file" "\n W = Save " "\n U = Save as " "\n " "\n Q = Quit the program" "\n\n Your choice: "; // // everything else unchanged (cf. solutions in Chapter 16) // return choice; } char askForSave() { char c; cout << "Do you want to save the phone list(y/n)? "; do { cin.get(c); c = toupper(c); }while( c != 'Y' && c != 'N'); return c; } 411 Overloading Operators Overloading operators allows you to apply existing operators to objects of class type. For example, you can stipulate the effect of the + operator for the objects of a particular class. This chapter describes various uses of overloaded operators. Arithmetic operators, comparisons, the subscript operator, and the shift operators for input and output are overloaded to illustrate the appropriate techniques. The concept of friend functions, which is introduced in this context, is particularly important for overloading operators. chapter 19 412 ■ CHAPTER 19 OVERLOADING OPERATORS The assignment operator =, the address operator &, and the comma operator, have a predefined meaning for each built-in type. This meaning can be changed for classes by a definition of your own. ✓ NOTE ■ GENERALS Overloadable operators ᮀ Rules An operator is always overloaded in conjunction with a class. The definition scope of an operator is simply extended—the characteristics of the operator remain unchanged. The following rules apply: ■ You cannot create “new operators”—that is, you can only overload existing oper- ators. ■ You cannot redefine the operators for fundamental types. ■ You cannot change the operands of an operator. A binary operator will always be binary and a unary operator will always be unary. ■ The precedence and the order of grouping operators of the same precedence remains unchanged. + - * / % == != < <= > >= & | ^ ~ << >> && || ! = op= () [] & * -> , new delete ++ Arithmetic operators Relational operators Logical operators Assignment operators (op is a binary arithmetic or a binary bitwise operator) Bitwise operators Function call, subscript operator Other operators Operators Meaning GENERALS ■ 413 ᮀ Overloading An operator is said to be overloaded if it is defined for multiple types. In other words, overloading an operator means making the operator significant for a new type. Most operators are already overloaded for fundamental types. In the case of the expression: Example: a / b the operand type determines the machine code created by the compiler for the division operator. If both operands are integral types, an integral division is performed; in all other cases floating-point division occurs. Thus, different actions are performed depend- ing on the operand types involved. ᮀ Operators for Classes In addition to defining methods, C++ offers the interesting possibility of defining the functionality of a class by means of operators. Thus, you can overload the + operator instead of, or in addition to, using the add() method. For the objects x and y in this class: x + y is equivalent to x.add(y) Using the overloaded operators of a class expressions of this type can be as easily defined as for fundamental types. Expressions using operators are often more intuitive, and thus easier to understand than expressions containing function calls. Many operators belonging to the C++ standard library classes are already overloaded. This applies to the string class, with which you are already familiar. Example: string str1("Hello "), str2("Eve"); str1 += str2; // Operator += if( str2 < "Alexa") // Operator < cout << str1; // Operator << str2[2] = 'i'; // Operators [] and = The tables on the opposite page show those operators that can be overloaded. Some operators cannot be overloaded, such as the cast operators, the sizeof operator, and the following four operators: . :: .* member access and scope resolution operators ?: conditional operator These operators either have a fixed significance in the classes for which they are defined, or overloading the operator makes no sense. 414 ■ CHAPTER 19 OVERLOADING OPERATORS // DayTime.h // The class DayTime containing operators < and ++ . // #ifndef _DAYTIME_ #define _DAYTIME_ class DayTime { private: short hour, minute, second; bool overflow; public: DayTime( int h = 0, int m = 0, int s = 0); bool setTime(int hour, int minute, int second = 0); int getHour() const { return hour; } int getMinute() const { return minute; } int getSecond() const { return second; } int asSeconds() const // Daytime in seconds { return (60*60*hour + 60*minute + second); } bool operator<( const DayTime& t) const // compare { // *this and t return asSeconds() < t.asSeconds(); } DayTime& operator++() // Increment seconds { ++second; // and handle overflow. return *this; } void print() const; }; #endif // _DAYTIME_ #include "DayTime.h" . . . DayTime depart1( 11, 11, 11), depart2(12,0,0); . . . if( depart1 < depart2 ) cout << "\nThe 1st plane takes off earlier!" << endl; . . . ■ OPERATOR FUNCTIONS (1) Operators < and ++ for class DayTime Calling the Operator < OPERATOR FUNCTIONS (1) ■ 415 ᮀ Naming Operator Functions To overload an operator, you just define an appropriate operator function. The operator function describes the actions to be performed by the operator. The name of an operator function must begin with the operator keyword followed by the operator symbol. Example: operator+ This is the name of the operator function for the + operator. An operator function can be defined as a global function or as a class method. Gener- ally, operator functions are defined as methods, especially in the case of unary operators. However, it can make sense to define an operator function globally. This point will be illustrated later. ᮀ Operator Functions as Methods If you define the operator function of a binary operator as a method, the left operand will always be an object of the class in question. The operator function is called for this object. The second, right operand is passed as an argument to the method. The method thus has a single parameter. Example: bool operator<( const DayTime& t) const; In this case the lesser than operator is overloaded to compare two DayTime objects. It replaces the method isLess(), which was formerly defined for this class. The prefix operator ++ has been overloaded in the example on the opposite page to illustrate overloading unary operators. The corresponding operator function in this class has no parameters. The function is called if the object a in the expression ++a is an object of class DayTime. ᮀ Calling an Operator Function The example opposite compares two times of day: Example: depart1 < depart2 The compiler will attempt to locate an applicable operator function for this expression and then call the function. The expression is thus equivalent to depart1.operator<( depart2) Although somewhat uncommon, you can call an operator function explicitly. The previ- ous function call is therefore technically correct. Programs that use operators are easier to encode and read. However, you should be aware of the fact that an operator function should perform a similar operation to the cor- responding operator for the fundamental type. Any other use can lead to confusion. 416 ■ CHAPTER 19 OVERLOADING OPERATORS // Euro1.h : The class Euro containing arithmetic operators. // #ifndef _EURO_H_ #define _EURO_H_ #include <sstream> // The class stringstream #include <iomanip> using namespace std; class Euro { private: long data; // Euros * 100 + Cents public: Euro( int euro = 0, int cents = 0) { data = 100L * (long)euro + cents; } Euro( double x) { x *= 100.0; // Rounding, data = (long)(x>=0.0 ? x+0.5 : x-0.5); //ex. 9.7 -> 10 } long getWholePart() const { return data/100; } int getCents() const { return (int)(data%100); } double asDouble() const { return (double)data/100.0; } string asString() const; // Euro as string. void print( ostream& os) const // Output to stream os. { os << asString() << " Euro" << endl; } // Operator functions Euro operator-() const // Negation (unary minus)) { Euro temp; temp.data = -data; return temp; } Euro operator+( const Euro& e2) const // Addition. { Euro temp; temp.data = data + e2.data; return temp; } Euro operator-( const Euro& e2) const // Subtraction. { /* Analog just as operator + */ } Euro& operator+=( const Euro& e2) // Add Euros. { data += e2.data; return *this; } Euro& operator-=( const Euro& e2); // Subtract euros. { /* Just as operator += */ } }; // Continued on the next double page. ■ OPERATOR FUNCTIONS(2) Class Euro OPERATOR FUNCTIONS(2) ■ 417 ᮀ Notes on the Sample Class Euro The opposite page shows the Euro class, which represents the new European currency. The member data stores a given amount of euros as an integer in the format: (integer part)*100 + Cents. Thus data/100 returns the number of euros and data%100 the number of cents. This technique allows for easy implementation of the arithmetic operations needed for the Euro class. In addition to a constructor that is passed whole euros and cents as arguments, there is a constructor that can process a double value of euros and a standard copy constructor. Example: Euro e1(9,50), e2(20.07), e3(-e1); ᮀ Negation, Addition, and Subtraction The unary operator - does not change its operand. In the previous example, e3 is thus assigned a value of -9,50 euro, but e1 remains unchanged. The operator function is thus a const method that creates and returns a temporary object. The binary operators + and - do not change their operands either. Thus, the operator functions also create temporary objects and return them with the correct values. Example: Euro sum = e1 + e2; The expression e1 + e2 results in e1.operator+(e2). The return value is used to initialize the new object, sum. ᮀ The += and -= Operators Although the operators + and - were overloaded for the Euro class, this does not auto- matically mean that the operators += and -= are overloaded. Both are distinct operators that require separate definitions. Of course, you should overload the operators to ensure that the statements Example: sum += e3; and sum = sum + e3; produce the same results. The binary operators += and -= change the current object, that is, the left operand. A temporary object is not required! The expression sum += e3 represents the current object after modification. Thus, the operator function returns a reference to *this. 418 ■ CHAPTER 19 OVERLOADING OPERATORS // Continues file Euro1.h // inline string Euro::asString() const // Euro as string { stringstream strStream; // Stream for conversion long temp = data; if( temp < 0) { strStream << '-'; temp = -temp; } strStream << temp/100 << ',' << setfill('0') << setw(2) << temp%100; return strStream.str(); } #endif // _EURO_H_ // Euro1_t.cpp // Tests the operators of class Euro. // #include "Euro1.h" // Definition of the class #include <iostream> using namespace std; int main() { cout << "* * * Testing the class Euro * * *\n" << endl; Euro wholesale( 20,50), retail; retail = wholesale; // Standard assignment retail += 9.49; // += (Euro)9.49 cout << "Wholesale price: "; wholesale.print(cout); cout << "Retail price: "; retail.print(cout); Euro discount( 2.10); // double-constructor retail -= discount; cout << "\nRetail price including discount: "; retail.print(cout); wholesale = 34.10; cout << "\nNew wholesale price: "; wholesale.print(cout); Euro profit( retail - wholesale); // Subtraction and // copy constructor cout << "\nThe profit: "; profit.print(cout); // Negative! return 0; } ■ USING OVERLOADED OPERATORS File Euro1.h continued Sample program . oper- ators. ■ You cannot redefine the operators for fundamental types. ■ You cannot change the operands of an operator. A binary operator will always be binary and a unary operator will always. understand than expressions containing function calls. Many operators belonging to the C++ standard library classes are already overloaded. This applies to the string class, with which you are already. ! = op= () [] & * -> , new delete ++ Arithmetic operators Relational operators Logical operators Assignment operators (op is a binary arithmetic or a binary bitwise operator) Bitwise operators Function