1.47k likes | 1.66k Views
CHAPTER 15 OVERLOADING AND TEMPLATES. In this chapter, you will: Learn about overloading Become aware of the restrictions on operator overloading Examine the pointer this Learn about friend functions Explore the members and nonmembers of a class Discover how to overload various operators
E N D
In this chapter, you will: • Learn about overloading • Become aware of the restrictions on operator overloading • Examine the pointer this • Learn about friendfunctions • Explore the members and nonmembers of a class • Discover how to overload various operators • Learn about templates • Explore how to construct function templates and class templates
The capability of combining data and operations on data is called encapsulation. This is the first principle of OOD. • In Chapter 12, we also defined abstract data type (ADT) and discussed how classes in C++ implement ADT. • In Chapter 13, we discussed how new classes could be derived from existing classes through the mechanism of inheritance and composition. • Inheritance is the second principle of OOD, as defined in Chapter 13, and encourages code reuse.
WHY OPERATOR OVERLOADING IS NEEDED • Recall the class clockType as defined in Chapter 12. • Consider the following statements. clockTypemyClock(8,23,34); clockTypeyourClock(4,5,30); myClock.printTime(); myClock.incrementSeconds(); if(myClock.equalTime(yourClock)) . . .
We would like to use the following statements in place of the above statements. cout<<myClock; myClock++; if(myClock == yourClock) . . .
The only built-in operations on classes are the assignment operator and the member selection operator. • Therefore, other operators cannot be applied, directly, on class objects. • C++ allows the programmer to extend the definitions of most of the operators to work with classes • In C++’s terminology, this is called operator overloading.
OPERATOR OVERLOADING • C++ allows the user to overload most of the operators to work effectively in a specific application. • C++ does not allow the user to create new operators. • Most of the existing operators can be overloaded to manipulate class objects. • In order to overload an operator we must write functions. • The name of the function that overloads an operator is the reserved word operator followed by the operator to be overloaded. • The following function name overloads >= operator>= • Operator function: The function that overloads an operator.
Syntax for Operator Functions The syntax of the heading of an operator function is returnType operator operatorSymbol(arguments) • The operator function is a value-returning function. • In C++, operator is a reserved word. • To overload an operator for a class: 1. Include the function to overload the operator (that is, the operator function) in the definition of the class. 2. Write the definition of the operator function.
Overloading an Operator: Some Restrictions When overloading an operator the following should be kept in mind. 1. You cannot change the precedence of an operator. 2. The associativity cannot be changed. (For example, the associativity of the arithmetic operator addition is from left to right and it cannot be changed.) 3. Default arguments cannot be used with an overloaded operator. 4. You cannot change the number of arguments that an operator takes. 5. You cannot create new operators. Only existing operators can be overloaded. The operators that cannot be overloaded are . .* :: ?: sizeof 6. The meaning of how an operator works with built-in types, such asint, remains the same. 7. Operators can be overloaded either for objects of the user-defined types, or for a combination of objects of the user-defined type and objects of the built-in type.
The following is a list of operators that can be overloaded. + - * / % ^ & | ! && || = == < <= > >= != += -= *= /= %= ^= |= &= << >> >>= <<= ++ -- ->* , -> [] () ~ newdelete
this Pointer • Every object of a class maintains a (hidden) pointer to itself and the name of this pointer is this. • In C++,thisis a reserved word. • When an object invokes a member function, thethispointer of the object is referenced by the member function. • Suppose that test is a class and it has a member function, say one. Further suppose that the definition of one looks like: test test::one() { . . . return *this; }
If x and y are objects of the type test, the statement y = x.one(); returns the value of the object x to object y, that is, the data members of x will be copied into the corresponding data members of y. • When the object x invokes the function one,the pointer this in the definition of the member function one refers to the object x and so this means the address of x and *thismeans the value of x.
Example 16-1 class thisPointerClass { public: void set(int a, int b, int c); void print() const; thisPointerClass updateXYZ(); //Post: x = 2 * x; y = y + 2; // z = z * z; thisPointerClass(int a = 0, int b = 0, int c = 0); private: int x; int y; int z; };
void thisPointerClass::set(int a, int b, int c) { x = a; y = b; z = z; } void thisPointerClass::print() const { cout<<"x = "<<x <<", y = "<<y <<", z = "<<z<<endl; } thisPointerClass thisPointerClass::updateXYZ() { x = 2 * x; y = y + 2; z = z * z; return *this; }
thisPointerClass::thisPointerClass(int a, int b, int c) { x = a; y = b; z = c; }
int main() { thisPointerClass object1(3, 5, 7); //Line 1 thisPointerClass object2; //Line 2 cout<<"Object 1: "; //Line 3 object1.print(); //Line 4 object2 = object1.updateXYZ(); //Line 5 cout<<"After updating object1: "; //Line 6 object1.print(); //Line 7 cout<<"Object 2: "; //Line 8 object2.print(); //Line 9 return 0; } Output Object 1: x = 3, y = 5, z = 7 After updating object1: x = 6, y = 7, z = 49 Object 2: x = 6, y = 7, z = 49
Example 16-2 class personType { public: void print() const; void setName(string first, string last); personType& setLastName(string last); //Function to set the last name //Post: lastName = last; //After setting the last name, a reference //to the object, that is, the address of the //object, is returned personType& setFirstName(string first); //Function to set the first name //Post: firstName = last; //After setting the first name, a reference //to the object, that is, the address of the //object, is returned void getName(string& first, string& last); personType(string first = "", string last = "");
private: string firstName; //store the first name string lastName; //store the last name }; personType& personType::setLastName(string last) { lastName = last; return *this; } personType& personType::setFirstName(string first) { firstName = first; return *this; }
int main() { personType student1("Angela", "Clodfelter"); //Line 1 personType student2; //Line 2 personType student3; //Line 3 cout<<"Line 4 -- Student 1: "; //Line 4 student1.print(); //Line 5 cout<<endl; //Line 6 student2.setFirstName("Shelly").setLastName("Malik"); //Line 7 cout<<"Line 8 -- Student 2: "; //Line 8 student2.print(); //Line 9 cout<<endl; //Line 10 student3.setFirstName("Chelsea"); //Line 11 cout<<"Line 12 -- Student 3: "; //Line 12 student3.print(); //Line 13 cout<<endl; //Line 14 student3.setLastName("Tomek"); //Line 15
cout<<"Line 16 -- Student 3: "; //Line 16 student3.print); //Line 17 cout<<endl; //Line 18 return 0; } Output Line 4 -- Student 1: Angela Clodfelter Line 8 -- Student 2: Shelly Malik Line 12 -- Student 3: Chelsea Line 16 -- Student 3: Chelsea Tomek
Friend Functions of Classes • A function that is defined outside the scope of a class is called a friend function. • A friend function is a nonmember function of the class, but has access to the private data members of the class. • To make a function friend to a class, the reserved word friend precedes the function prototype (in the class definition). • The word friend appears only in the function prototype (in the class definition), not in the definition of the friend function.
class classIllusFriend { friendvoid two(...); . . . }; • In the definition of the classclassIllusFriend, two is declared as a friend of the class classIllusFriend; that is, it is a nonmember function of the classclassIllusFriend. • When you write the definition of the function two, any object of the type classIllusFriend—which is either a local variable of two or a formal parameter of two—can access its private members within the definition of the function two. • Because a friend function is not a member of a class, its declaration can be placed within the private, protected, or public part of the class.
Definition of a friend Function • When writing the definition of the friend function, the name of the class and the scope resolution operator does not precede the name of the friend function in the function heading. • The definition of the function two in the above class classIllusFriend is: void two(...) { . . . } • We will place the definition of the friend function in the implementation file.
Example 16-3 class classIllusFriend { friendvoid two(classIllusFriend cLFObject); public: void print(); void setx(int a); private: int x; }; void classIllusFriend::print() { cout<<"In class classIllusFriend: x = "<<x<<endl; } void classIllusFriend::setx(int a) { x = a; }
void two(classIllusFriend cLFObject) //Line 1 { classIllusFriend localTwoObject; //Line 2 localTwoObject.x = 45; //Line 3 localTwoObject.print(); //Line 4 cout<<endl; //Line 5 cout<<"Line 6: In Friend Function two accessing " <<"private data member x " <<localTwoObject.x<<endl;//Line 6 cLFObject.x = 88; //Line 7 cLFObject.print(); //Line 8 cout<<endl; //Line 9 cout<<"Line 10: In Friend Function two accessing " <<"private data member x " <<localTwoObject.x<<endl;//Line 10 }
int main() { classIllusFriend aObject; //Line 11 aObject.setx(32); //Line 12 cout<<"Line 13: aObject.x: "; //Line 13 aObject.print(); //Line 14 cout<<endl; //Line 15 cout<<"*~*~*~*~*~* Testing Friend Function " <<"two *~*~*~*~*~*"<<endl<<endl; //Line 16 two(aObject); //Line 17 return 0; }
Output Line 13: aObject.x: In class classIllusFriend: x = 32 *~*~*~*~*~* Testing Friend Function two *~*~*~*~*~* In class classIllusFriend: x = 45 Line 6: In Friend Function two accessing private data member x 45 In class classIllusFriend: x = 88 Line 10: In Friend Function two accessing private data member x 45
Operator Functions as Member Functions and Nonmember Functions 1. The function that overloads any of the operators (), [], ->, or = for a class must be declared as a member of the class. 2. Suppose an operator op is overloaded for a class, say OpOverClass. a. If the leftmost operand of op is an object of a different type (that is, not of the type OpOverClass), the function that overloads the operator op for OpOverClass must be a nonmember—that is, a friend of the classOpOverClass. b. If the operator function that overloads the operator op for the classOpOverClass is a member of the classOpOverClass, then when applying op on objects of the type OpOverClass, the leftmost operand of op must be of the type OpOverClass.
Except for some of the operators, as explained above, most of the operators can be overloaded either as member functions or as nonmember functions. • The following class is used to illustrate operator overloading. class OpOverClass { . . . private: int a; int b; }; OpOverClass x; OpOverClass y; OpOverClass z;.
Binary Operators as Member Functions • Suppose that the binary operator + is overloaded for the classOpOverClass. Overloading + as a Member Function • Suppose that + is overloaded as a member function of the classOpOverClass. • The name of the function to overload + for the classOpOverClass is operator+
Since x and y are objects of the type OpOverClass, we can perform the operation x + y • The compiler translates this expression into the following expression: x.operator+(y) • In this expression, there is only one argument to the function operator+, which is y. • In the above statement, operator+ has direct access to the private members of the object x. • The first argument to operator+ is the object that is invoking the function operator+ and the second argument is passed as a parameter to this function.
Suppose that the operator + adds the corresponding data members of the objects. • The result of the expression x + y is an object of the type OpOverClass. • The return type of the operator function operator+ is of the type OpOverClass. • The function prototype of operator+ in the definition of the class OpOverClass is: OpOverClass operator+(const OpOverClass&) const;
OpOverClass OpOverClass::operator+ (const OpOverClass& otherObject) const { OpOverClass temp; temp.a = a + otherObject.a; temp.b = b + otherObject.b; return temp; }
General Syntax to Overload Binary (Arithmetic) Operators as Member Functions Function Prototype (to be included in the definition of the class): returnType operator op(className); • op stands for the binary operator to be overloaded. • returnType is the type of the value returned by the function. • className is the name of the class for which the operator is being overloaded.
Function Definition: returnType className::operator op (const className& otherObject) const { //algorithm to perform the operation return (value); }
Example 16-4 class OpOverClass { public: void print() const; //Overload arithmetic operators OpOverClass operator+(const OpOverClass&) const; OpOverClass operator*(const OpOverClass&) const; OpOverClass(int i = 0, int j = 0); private: int a; int b; }; void OpOverClass::print() const { cout<<"("<<a<<", "<<b<<")"; }
OpOverClass::OpOverClass(int i, int j) { a = i; b = j; } OpOverClass OpOverClass::operator+ (const OpOverClass& right) const { OpOverClass temp; temp.a = a + right.a; temp.b = b + right.b; return temp; } OpOverClass OpOverClass::operator* (const OpOverClass& right) const { OpOverClass temp; temp.a = a * right.a; temp.b = b * right.b; return temp; }
int main() { OpOverClass u(23, 45); //Line 1 OpOverClass v(12,10); //Line 2 OpOverClass w1; //Line 3 OpOverClass w2; //Line 4 cout<<"Line 5: u = "; //Line 5 u.print(); //Line 6; output u cout<<endl; //Line 7 cout<<"Line 8: v = "; //Line 8 v.print(); //Line 9; output v cout<<endl; //Line 10 w1 = u + v; //Line 11; add u and v cout<<"Line 12: w1 = "; //Line 12 w1.print(); //Line 13; output w1 cout<<endl; //Line 14
w2 = u * v; //Line 15; multiply u and v cout<<"Line 16: w2 = "; //Line 16 w2.print(); //Line 17; output w2 cout<<endl; //Line 18 return 0; } Output Line 5: u = (23, 45) Line 8: v = (12, 10) Line 12: w1 = (35, 55) Line 16: w2 = (276, 450)
Overloading Relational Operators as Member Functions • We overload the equality operator, ==, for the class OpOverClass. • Since the result of a relational operator is either true or false, the returnType of the function operator== is Boolean. • The syntax of the function prototype to be included in the definition of the class OpOverClass is: booloperator==(const OpOverClass&) const;
bool OpOverClass ::operator== (const OpOverClass& right) const { return(a == right.a && b == right.b); }
General Syntax to Overload Binary Relational Operators as Member Functions Function Prototype booloperator op(const className&) const; • op is the relational operator that is being overloaded. • className is the name of the class for the operator op is being overloaded. Function Definition: bool className::operator op(const className& right) const { //Compare and return the value }
Binary Operators as Nonmember Functions • Suppose that + is overloaded as a nonmember function of the classOpOverClass. • Suppose that the following operation is to be performed. x + y • This expression is compiled as operator+(x,y) • The function operator+ has two arguments. • The function operator+ is neither a member of the object x nor a member of the object y. • The objects to be added are passed as arguments to the function operator+.
To include the operator function operator+ as a nonmember of the class in the definition of the class the reserved word friend must appear before the function heading. • The function operator+ must have two arguments. • To include operator+ as a nonmember in the definition of the the classOpOverClass its prototype in the definition of OpOverClass is friend OpOverClassoperator+(const OpOverClass&, const OpOverClass&);
OpOverClass operator+( const OpOverClass& firstObject, const OpOverClass& secondObject) { OpOverClass temp; temp.a = firstObject.a + secondObject.a; temp.b = firstObject.b + secondObject.b; return temp; }
In the above definition, we add the corresponding data members of firstObject and secondObject and store the result in temp. • Recall that the private members of a class are local to the class and therefore cannot be accessed outside the class. • Following this rule, since operator+ is not a member of the classOpOverClass, in the definition of the function operator+, expressions such as firstObject.a must be illegal since a is a private member of firstObject. • Since operator+ was declared as a friend function of the classOpOverClass, an object of the type OpOverClass can access its private members in the definition of operator+. • In the function heading, the name of the class, that is, OpOverClass and the scope resolution operator is not included before the name of the function operator+ since the function operator+ is not a member of the class.
General Syntax to Overload Binary (Arithmetic) Operators as Nonmember Functions Function Prototype (to be included in the definition of the class): friend returnType operator op(const className&, const className&); • op stands for the binary operator to be overloaded. • returnType is the type of the value returned by the function. • className is the name of the class for which the operator is being overloaded.
Function Definition returnType operator op(const className& firstObject, const className& secondObject) { //algorithm to perform the operation return (value); }
Overloading Relational Operators as Nonmember Functions • We overload the equality operator, ==, for the classOpOverClass. • The return type of the function operator== is Boolean. Function Prototype friendbooloperator==(const OpOverClass&, const OpOverClass&);
Function Definition booloperator==(const OpOverClass& firstObject, const OpOverClass& secondObject) { return(firstObject.a == secondObject.a && firstObject.b == secondObject.b); }