slides01

Operator Overloading: Operator Overloading
Objectives: Objectives At the end of this lesson, students should be able to: Write programs that correctly overload operators Describe how to overload a unary operator Tell how the compiler differentiates between pre- and post Describe how to overload a binary operator Explain when to use member vs non-member functions Explain when it is necessary to use friend functions Demonstrate how to return values when overloading operators Explain why operator overloading is useful Explain what the compiler generates when it sees an overloaded operator Demonstrate how constructors and overloaded cast operator are used to do conversions
Operator Overloading: In C++, operators are nothing more than functions, written with a different, but more familiar syntax. Operator Overloading
Slide4: We use operators to express fundamental operations in a concise and readable way. For example, y = x + y *z; is much easier to read and write than y.assign (x.add (y.multiply(z)));
Slide5: The standard operators in C++ work on all of the basic data types. However, they do not work on user defined data types. To add two objects of the Length class, we could write a function called add, so we could write lengthThree = lengthTwo.add (lengthOne); But, this is awkward. Wouldn’t it be nicer to be able to write lengthThree = lengthTwo + lengthOne;
Slide6: We can do this if we overload the + operator so that it works with objects of the Length class.
Example: Example
Slide8: Define the Length class as … class Length { public: Length( ); private: int feet; int inches; }; The Length class keeps track of a length measurement in feet and inches.
Slide9: To overload the + operator so that we can add two Length objects together, we write a function of the form: Length operator+ (const Length&); in this case the function returns a Length object. the name of the function is operator+ the function takes a const reference to a Length object as a parameter.
Slide10: Now, if we write lengthThree = LengthTwo + LengthOne; The compiler looks in the Length class for a function with the prototype Length ::operator+ (const Length&);
Slide11: and generates code just as if we had written lengthThree = lengthTwo.operator+ (lengthOne); the left hand operand becomes the calling object. the right hand operand becomes the parameter in the function call.
Slide12: All of the C++ operators can be overloaded except for the following: . .* :: ?: sizeof Because of the complexities involved, the overloading of &&, ||, and the comma operator is discouraged.
Key points to remember: Key points to remember You cannot change the precedence rule by overloading operators. You cannot change the associativity of an operator by overloading it. It is not possible to create new operators. You cannot change the way that the operators work on the basic data types. You cannot change the “arity” of an operator by overloading it.
Overloading Unary Functions: Overloading Unary Functions It certainly ought to be possible to overload the increment and decrement operators for the Length class. For example, we would probably like to be able to do the following for a length object lengthOne: lengthOne++;
The “makes sense” Rule: The “makes sense” Rule A programmer can clearly define what to do inside of the function that overloads an operator. However, it is just good programming practice to be sure that the function makes good sense.
Slide16: So … what does it mean to increment a Length?
Slide17: Let’s choose the option of adding one inch to the current Length value. This points out a complexity that we may not have thought of before. Suppose we have a Length object whose value is 3 feet and 11 inches. What happens when we increment this object. Obviously we want the result to be 4 feet. This complicates the code we would write, because we always have to check to see if the addition of one inch results in going to a new number of feet.
Slide18: Let’s simplify the problem by re-defining the Length class and take advantage of data hiding. class Length { public: Length( ); private: int inches; }; Because we only keep track of inches, we don’t have to worry here (or in any other arithmetic operation) about handling the overflow from inches to feet.
Slide19: Now the code for the operator++ function would look something like: const Length& Length::operator++( ) { ++inches; return *this; } when written as a member function, functions that overload unary operators take no parameters. the function works on the calling object. In this case, the inches data member of the calling object is incremented. we return the calling object (by reference).
Return Types: Return Types In the increment example, why did we return a Length object, and why by reference? In any expression, the result of some operation may be used as an operand for another operator. Consider the expression Length someLength = ++lengthOne; We pass by reference for efficiency!
More Complications!: More Complications! There are a couple of other issues to consider in this case. 1. How do we differentiate between a pre- and a post-increment? 2. Should we write the operator++ function as member function or as a non-member function?
pre- or post-increment: pre- or post-increment The code we just examined is for the pre-increment operator. To tell the compiler that we want the function to be for the post-increment operator, we put a dummy parameter in the function prototype, as : Length operator++ ( int ); this parameter is never actually used. It is simply a flag to the compiler that this function is overloading the post-increment operator.
Slide23: Length Length::operator++ (int) { Length tl = *this; ++inches; return tl; } What’s going on here???
Slide24: calling object, myLength inches 5 myLength++; 6 a copy of the object tl is put on the stack and returned to the calling program. Why can’t we return a reference here?
Slide25: cout << myLength++; the result is that the copy of the original object (with the original value of inches) is sent to cout. But, the inches value of myLength is equal to 6. The effect is that myLength gets incremented after (post-increment) the << operator is executed.
Member vs. Non-member: Member vs. Non-member When overloading ( ), [ ], ->, or any of the assignment operators, the overloading operator function must be written as a member function. For all other operators, we may write the function as a member or a non-member function. If the lefthand operand is a basic data type, or an object of a different class than the operator’s class, then the operator overloading function must be a non-member function. Non-member functions can be written as friend functions.
Friends: Friends Friend functions are non-member functions that have all of the privileges of member functions. In particular, a friend function can access private data in the class that it is a friend of.
Slide28: Friendship must be given! class Length { public: Length( ); friend Length& operator++ (Length& t); private: int inches; }; the keyword friend tells the compiler that the function operator++ is a friend of the class. It is a non-member function. The function has access to the private data inches.
Slide29: Length operator++ (Length& t) { t.inches++; return t; } the function goes in a .cpp file. Note that there is no scope resolution operator nor a class name because the function does not belong to the class. the code inside the function can directly access the inches data member, even though it is private. don’t include the keyword friend here. The function cannot claim it is a friend. This is done in the class definition. because it is a non-member function, we must pass the object to be worked on as a parameter.
Slide30: Object Oriented purists don’t like friend functions because the break the rules of encapsulation and data hiding. If you want to be pure, provide accessor functions in the class and invoke them from the non-member function. However, this is less efficient than using a friend function.
Friend Classes: Friend Classes A class can be a friend of another class. When a class is declared as a friend, all of its member functions can access the private data of the class it is a friend of.
Overloading Binary Operators: Overloading Binary Operators A binary operator takes two operands. We will refer to these as the left-hand operand and the right-hand operand. a = b + c; operator left-hand operand right-hand operand
As a Member Function: As a Member Function Length Length::operator+ (const Length& rh) { Length tLen; tLen.inches = inches + rh.inches; return tLen; } functions that overload binary operators, written as member functions, take one parameter, the right-hand operand. this value comes from the calling, or implicit object, the left-hand operand. the result of adding two Length objects ought to be a Length object.
Slide34: a = b + c; operator left-hand operand right-hand operand for a, b, and c all Length objects … b. operator+ (c); The compiler generates the function invocation …
Nameless Temporary Objects: Nameless Temporary Objects In the evaluation of the expression a = b + c; a Length object is returned on the stack. This object has no name, and is oftentimes called a nameless temporary object (nto). finally the assignment is done. By default, each data member of the nto is copied into the corresponding data member of the object a. after completing the assignment, the nto is removed from the stack and no longer exists.
Commutative Operators: Commutative Operators + is a commutative operator. That is, b + c; is the same as c + b;
Slide37: But what if one operand is a basic data type, for example, an integer. This probably makes sense, since, for example, we can think of adding an integer, like 5, to a Length. To make the operator commutative, we should be able to write Length c = myLength +5; as well as Length c = 5 + myLength; but … how do we write the function in this case?
Slide38: Rule of thumb: If the piece of data that you would normally send the message to (the left-hand operand) is a basic data type, then you have to overload the operator by writing a non-member function!
As a Non-member Function!: As a Non-member Function! Length operator+ ( int lh, Length rh) { Length tLength; tLength.inches = lh + rh.inches; return tLength; } functions that overload binary operators take two operands when written as a non- member function.
Overloading << and >>: Overloading << and >> << and >> are binary operators where the left-hand operator is always of a different class than the one for which we are overloading the operator. For example, cout << myLength; so .. we must write these as non-member functions.
Slide41: Because we want to represent a Length as feet and inches externally, we must do some conversions. As a result, the function to overload the << operator might look like: ostream &operator<< (ostream &out, const Length &rh) { int ft = rh.inches/12; int in = rh.inches %12; out << ft << “ft., “ << in << “in”; return out; } out must be passed by reference because the ostream class has no copy constructor. convert inches to feet and inches. the stream is returned so that we can cascade the << operator.
Cascading <<: Cascading << out << ft << “ft., “ << in << “in”; this expression is evaluated first, and returns the stream out. The entire expression may now be thought of as out << “ft., “ << in << “in”; where out already contains out 5
Slide43: this expression is evaluated next, and returns the stream out. The entire expression may now be thought of as out << “ft., “ << in << “in”; where out already contains out 5 ft. out << in << “in”;
Slide44: this expression is evaluated next, and returns the stream out. The entire expression may now be thought of as out << in << “in”; where out already contains out 5 ft. 3 out << “in”;
Slide45: finally this expression is evaluated and returns the stream out. The return value is not used in this last instance. out << “in”; out now contains out 5 ft. 3 in.
Stream Extraction: Stream Extraction Overloading the Stream Extraction operator is similar. Here, we have to decide how the user will enter in the length. In this example, I have assumed that the user will enter the number of feet, followed by white space, and then the number of inches (I would probably prompt the user to enter the data this way).
Slide47: istream &operator>> (istream &input, Length &rh) { int ft, in; input >> ft; input >> in; rh.inches = in + 12 * ft; return input; }
Conversions and Overloading: Conversions and Overloading When converting from a basic data type to a user defined class, the compiler looks for a constructor in the class that takes the base data type as a parameter. Length::Length (const float r) { inches = r * 12; }
Slide49: Now, if the programmer writes myLength = 5.3; the compiler invokes the constructor to create a nameless temporary object (nto). The assignment from the nto to the object myLength is then performed and the nto is thrown away. Note: 5.3 is in feet!
Slide50: When converting from a user defined class to a base data type, the compiler looks for a function that overloads the cast operator. Length::operator float( ) { int ft, in; ft = inches/12; in = inches %12; float r = (float)in/12.0; return ft + r; } notice that no return type is declared. The return type is derived from the type of cast being done.
Slide51: Now if the programmer writes float r = myLength; The compiler invokes the function written to overload the cast operator to do the conversion.
Slide52: When converting from an object of one class to an object of another class, an explicit cast is not always required. A statement like orangeObject = appleObject; will cause the compiler to look for a constructor in the Orange class that takes an Apple object as a parameter, or a function in the Apple class that overloads the cast operator to return an Orange object.
Slide53: If both a constructor and a cast operator are defined, the compiler will give an ambiguity error. It does not know which you want it to use.

bilder/01

These presentations are classified and categorized, so you will always find everything clearly laid out and in context.
You are watching Я - специалист presentation right now. We are staying up to date!