Presentation is loading. Please wait.

Presentation is loading. Please wait.

Copyright  Hannu Laine C++-programming Part 4: Operator overloading.

Similar presentations


Presentation on theme: "Copyright  Hannu Laine C++-programming Part 4: Operator overloading."— Presentation transcript:

1 Copyright  Hannu Laine C++-programming Part 4: Operator overloading

2 HL1 Main criteria is that member function should be easy to use (most natural and easy way to call it). We use Point class (discussed earlier) as an example. The class definition is now class Point { public: Point(float x0=0.0, float y0=0.0); //What is the prototype of add like void print(const char *explanation) const; private: float x; float y; } ; //Application void main(void) { Point p1(1.0, 1.0), displacement(10.0, 10.0), p2; //How to add displacement to p1 to get p2; p2.print("The destination is "); } Remark. Remember that we have two different views: 1) Application programmer’s view and 2) Component programmer’s view. Using member function vs. Implementing member function

3 HL2 Main criteria is that member function should be easy to use (most natural and easy way to call it). Using member function vs. Implementing member function Example. How to add a displacement (  x,  y) to a coordinate point to get a new location: Point p1, destination, p2; Way 1: add(p1, displacement, &p2); Way 2: p2 = add(p1,displacement); Way 3: p2.add(p1, displacement); // C++ Way 4: p2 = p1.add(displacement); //C++ Way 5: displacement.add(p1, &p2); //C++ Way 6: p1.add(displacement, &p2); //C++ Way 7: p2 = p1 + displacement; //C++... Which way is the most natural and easy to understand? When the way to call the function is decided you can derive the function prototype. For example, if you prefer way 4, the function prototype should be: Point Point::add(const Point &displacement) const;

4 HL3 Often the way 7 is regarded as the best way. To make it work operator overloading of C++ is needed. Basically the implementations of way 4 and way 7 are very similar. The only difference is that it is possible to make the compiler generate a function call using operator (+ operator is this case). Way 7 can be made working in the following way: Function prototype inside the class definition is: Point operator+(const Point &displacement) const; The implementation of that function is: Point Point::operator+( const Point &displacement) const {Point destination; destination.x = x + displacement.x; destination.y = y + displacement.y; return destination; } Operator overloading compare to function add The operator function can be used in the following way: destination = origin + displacement; The interpretation of the compiler is destination = origin.operator+(displacement); Almost all operators can be overloaded. For example Arithmetic operators : +, -, *, / Relational operators :, >= Assignment operator = and indexing operator [ ]

5 HL4 We saw how to implement operator + as a member function. It is not always possible to use member function in the operator overloading. Example 1. Output operator << is overloaded for all build-in types as a member function of the class ostream, because the expression int a = 1; cout << a can be interpreted as cout.operator<<(a); If we want to overload output operator for our own class like Point it is not possible to define it as a member function because we want to use it as follows Point p; cout << p; This means that it must be the member function of class ostream. But ostream is fully tested library class. It is not wise to modify it! Example 2. In this second example we have an object of class Counter. Operator + that adds integer to the counter can be overloaded as a member function and used as follows c + 1(member function ) If we want to add counter to integer in the following way, it is not possible with a member function 1 + c (this is possible only using friend function). Operator overloading as friend function

6 HL5 The idea of friend function is first illustrated using simplified example: class C { friend void f(C &c); public: void member_func(); private: int data_mem; }; void main(void) { C c; f(c); // OK //c.data_mem = 10; syntax error, because // data_mem is private //c.f(c); syntax error, because f is not a member c.member_func(); //OK } void f(C &c) { c.data_mem = 10; //is allowed because f is }// a friend of C Operator overloading as friend function Note that friend function is not a member. This means that keyword public or private means nothing for friend function. It also means there is no “target” object in the call.

7 HL6 And now back to the example (+ operator for Point). Function prototype inside the class definition is: friend Point operator+(const Point &p, const Point &displacement); The implementation of that function is: Point operator+(const Point &p, const Point &displacement) { Point destination; destination.x = p.x + displacement.x; destination.y = p.y + displacement.y; return destination; } Operator overloading as friend function The operator function still can be used in the following way: destination = origin + displacement; The interpretation of compiler is destination = operator+(origin, displacement);

8 HL7 Complete example 1/2 #include //class definitions class Point { // friend Point operator+(const Point &p, //const Point &displacement); public: Point(float x0=0.0, float y0=0.0); void read(const char *prompt ); void print(const char *explanation) const; Point operator+(const Point &displacement) const; private: float x; float y; } ; //Application void main(void) { Point p1(1.0, 1.0), displacement, p2; displacement.read("Enter displacement delta x and y\n”); p2 = p1 + displacement; p2.print("The destination is "); } //Operation function implementations Point::Point(float x0, float y0) { x = x0; y = y0; }

9 HL8 Complete example 2/2 void Point::read(const char *prompt) { cout << prompt; cout << "Enter x: "; cin >> x; cout << "Enter y : "; cin >> y; } void Point::print(const char *explanation) const{ cout << explanation; cout << "(" << x << ", " << y << ")"; } //Option 1: Member function of the class Point Point::operator+(const Point &displacement) const{ Point destination; destination.x = x + displacement.x; destination.y = y + displacement.y; return destination; } //Option 2: Friend function of the class /* Point operator+(const Point &p, const Point &displacement) { Point destination; destination.x = p.x + displacement.x; destination.y = p.y + displacement.y; return destination; } */

10 HL9 All objects should be made assignable. Assignment is defined for all objects by default (compiler generates default assignment). Default assignment is not satisfactory, if classes have dynamic data members. The problem is actually the same as we learned why copy constructors are needed. We still use the following Person class as an example with the following definitions and implementations. //Class definition class Person { public: Person(const char *name0="", int age0=0); ~Person(); void print(); private: char *name; int age; }; // Implementations of constructor and destructor Person::Person(const char *name0, int age0){ name = new char[strlen(name0) + 1]; strcpy(name,name0); age = age0; } Person::~Person(){ delete name; } Overloading assignment operator 1/3

11 HL10 The following example shows the problems that arise when default assignment is used: Example. (Class Person has name as a dynamic member.) void main(void) { Person p1(“Matti”, 20); { Person p2; p2 = p1; //Problem 1: Memory leak ! p2.setName(“XXX”); p1.print(); //Problem 2: Name of p1 is XXX ! } p1.print(); // Problem 3: Name is undefined ! } // Problem 4: Double deletion ! The default assignment is the reason for problems in the example program above. To fix the problem (to make the class fail safe) we have to write our own assignment operator. See the next page. Overloading assignment operator 2/3

12 HL11 How to do it for class Person 1) Prototype inside the class definition const Person& operator=(const Person &p); 2) Implementation const Person& Person::operator=(const Person &p){ if (this != &p) {//avoid damages in self assignment delete name; name = new[strlen(p.name) + 1]; strcpy(name, p.name); age = p.age; } return *this; } 3) How it is interpreted? Person p1(“Matti”, 20), p2; p2 = p1; The compiler interprets this as p2.operator=(p1); 4) Why this function returns const reference to the target? Overloading assignment operator 3/3

13 HL12 The input operator >> and output operator << can be overloaded for our own classes, so that it is easy to read them from keyboard and display them on the display. How should the programmer interpret the following notations: int a;doudle b; char c[3]; cin >> a;// interpreted as cin.operator<<(a); cin >> b; // interpreted as cin.operator<<(b); cin >> c; // interpreted as cin.operator<<(c); cin >> a >> b; // interpreted as // ( cin.operator >(b) Why these notations are possible? Note that operator overloading, function overloading and reference parameter concepts are all needed here. How to make the same notations possible for our own classes? Why do we need to use friend function in this case? See an example program on the next page. Overloading input/output operators

14 HL13 Overloading > for user defined classes #include class Point { //class definition friend istream &operator>>(istream &in, Point &p); friend ostream &operator<<(ostream &out, const Point &p); public: Point(float x0=0.0, float y0=0.0); private: float x; float y; } ; //Application void main(void) { Point p; cout << ”Enter point ”; cin >> p; cout << ”The point is ”<< p; } //Operation function implementations Point::Point(float x0, float y0) { x = x0; y = y0; } //Friend functions istream &operator>>(istream &in, Point &p) { cout << ”Enter x and y ”; in >> p.x >> p.y; return in; } ostream &operator<<(ostream &out, const Point &p) { out << ”(” << p.x << ”,” << p.y << ”)”; return out; }

15 HL14 All operators so far have been binary operators. This means that they take two operands. There are also unary operators that have only one operand. Operator can precede or follows it’s operand. Some examples of unary operators are: ! (not), ~ (complement), ++ (pre- and post- increment) The unary operators can be overloaded too. Now we overload unary operator ++ as a pre- increment operator and as a post-increment operator. Before that, let’s recall the semantics of these operators using a simple example: int main(void) { int i = 0; cout << i++; // output is 0 cout << ++i; // output is 2 } In this example the operation where i is used is output (display) operation. So, in the case of post- increment the I is first displayed and after that incremented. In the case of pre-increment the I is first incremented and after that displayed. Overloading unary operators 1/4

16 HL15 Now we want to overload these operators in our own class Counter. The definition of the class Counter is class Counter { friend ostream& operator<<(ostream &out, const Counter&c) public: Counter(int c0 = 0); Counter& operator++(); // pre-increment Counter operator++(int); // post-increment private: int count; }; Overloading unary operators 2/4

17 HL16 Pre-increment operator: Prototype inside the class definition: Counter& operator++(); How to use it? Counter counter; ++ counter; How does compiler interpret it? counter.operator++(); The basic idea is that compiler calls member function operator++ without parameters. How to implement it? Counter& Counter::operator++() { count++: return *this; } Overloading unary operators 3/4

18 HL17 Post-increment operator: Prototype inside the class definition Counter operator++(int); How to use it? Counter counter; counter++; How does compiler interpret it? counter.operator++(0); The basic idea is that compiler calls member function operator++ with integer parameter. This parameter is so called dummy parameter and it is used only to make difference between pre increment and post increment operator. How to implement it? Counter Counter::operator++(int) { Counter old = *this; count++: return old; } Overloading unary operators 4/4

19 HL18 1. Conversion from other type to class object: Constructor can be so called conversion constructor, that works as a conversion operator. For example the constructor Counter (int n0 = 0) of class Counter can be used as a conversion (type casting) operator int -> Counter as follows: Counter c = 10; //is like Counter c(10); initialize c = 20; //is like c = Counter(20); conversion and // assignment 2. Conversion from class object to some other type: We have to define a type cast operator. For example, to do conversionCounter -> int we need operator int Prototype inside the class definition: operator int(); How to use it? Counter c; int i; i = c; // interpreted as i = c.operator int(); It is interesting to study why and how the following program fragment works (see the complete program in handouts). Now we assume that only the first conversion above (constructor) is defined. Counter i; for (i = 0; i < 5 ; i++) cout << i; Conversion operators

20 HL19 The goal of this example is to give an additional example of operator overloading, give an example of overloading indexing operator, to demonstrate the returning of reference, to give a foundation for understanding containers in STL. Disadvantages of arrays in C Accidental over indexing is possible (index out of range). Size is fixed (increasing the size requires many user operations (allocate, copy, release). Assignment is not possible. Comparison is not possible. We want to develop a better array that can be used like C-like arrays (without the problems). This is possible by creating an array class. Before going to the class definition we learn the concept of returning a reference. Introduction to intelligent arrays

21 20 Returning a reference (again) Function can return a value 1.Function f1 has a prototype int f1(); This function returns a value (of integer). This value can be used only as “Right value”. int a; a = f1(); //OK right value f1() = 10; //NOT OK left value Function can return a pointer 2.Function f2 has a prototype int *f2(); This function returns a pointer to integer. This pointer can be used (by referencing) to get the value of int (Right value of int) or to set or modify the value of int (Left value). int a; a = *f2(); //OK right value *f2() = 10; //OK left value Function can return a reference. 3. Function f3 has a prototype int &f3(); This function returns a reference to integer. This reference can be used directly to get the value of int (Right value of int) or to set or modify the value of int (Left value). int a; a = f2(); //OK right value f2() = 10; //OK left value Example where comparison is made between returning a reference and returning a pointer:

22 HL21 // Class IntelligentArray #include inline int minimum(int a,int b) { return a<b ? a : b; } //Header of class intelligent array class IntelligentArray { private: float *array; int capacity; //size of allocated space public: IntelligentArray(int initial_size=5, float init_value=0.0); int size() const; void resize(int new_size); float &operator[]( int i); }; //Main program int main(void) { IntelligentArray array(5, 0);//initial size 5, all elements to 0 int i; cout << "\n The size is " << array.size(); for ( i = 0 ; i < 12 ; i++) { if ( i >= array.size()) { array.resize(array.size() + 5); cout << "Size is incremented by 5" << endl; } array[i] = i; // interpreted as array.operator[ ] (i) = i; } cout << "\n The size is " << array.size() << endl; for (i = 0; i < 12 ; i++ ) cout << array[i] << endl; return 0; } ”Intelligent” array (1)

23 HL22 //Implementation of intelligent array IntelligentArray::IntelligentArray(int initial_size, float init_value) { capacity = initial_size; array = new float[initial_size]; for (int i = 0 ; i < initial_size ; i++) array[i] = init_value; } int IntelligentArray::size()const{ return capacity; } void IntelligentArray::resize(int new_size){ int min, i; float *new_array; min = minimum(capacity, new_size); new_array = new float[new_size]; for (i = 0 ; i < min ; i++) new_array[i] = array[i]; if (new_size > capacity) for ( i = capacity ; i < new_size ; i++) new_array[i] = 0.0; delete array; array = new_array; capacity = new_size; } float& IntelligentArray::operator[](int i) { if (0<=i && i < capacity) return array[i]; cout << "Exception handling is needed here"; ”Intelligent” array (2)

24 HL23 OperatorAssociativity () [] ->.Left to right ! ~ + - ++ -- & * (type)Right to left (unary) * / %Right to left + - Left to right > Left to right >= Left to right == != Left to right & Left to right (bit-wise and) ^ Left to right (bit-wise xor) | Left to right (bit-wise or) && Left to right (logical and) || Left to right (logical or) ? : (condition) Right to left = *= /= %= += -= &= ^= |= >= Right to left, Left to right Operator precedence


Download ppt "Copyright  Hannu Laine C++-programming Part 4: Operator overloading."

Similar presentations


Ads by Google