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

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

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

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

Copyright Hannu Laine

C++-programming

Part 4: Operator overloading

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

HL 2

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 likevoid 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

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

HL 3

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;

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

HL 4

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 [ ]

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

HL 5

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-intypes as a member function of the class ostream,because the expression

int a = 1;cout << a

can be interpreted ascout.operator<<(a);

If we want to overload output operator for our ownclass like Point it is not possible to define it as amember function because we want to use it asfollows

Point p;cout << p;

This means that it must be the member function ofclass ostream. But ostream is fully tested libraryclass. It is not wise to modify it!

Example 2. In this second example we have an object of classCounter. Operator + that adds integer to thecounter can be overloaded as a member function

and used as followsc + 1 (member function )

If we want to add counter to integer in the followingway, it is not possible with a member function

1 + c (this is possible only using friend function).

Operator overloading as friend function

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

HL 6

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 memberc.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.

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

HL 7

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);

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

HL 8

Complete example 1/2

#include <iostream.h>//class definitionsclass 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;

} ;//Applicationvoid 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 implementationsPoint::Point(float x0, float y0) {

x = x0;y = y0;

}

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

HL 9

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 classPoint 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;

} */

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

HL 10

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 definitionclass Person { public: Person(const char *name0="", int age0=0); ~Person(); void print(); private: char *name; int age;};

// Implementations of constructor and destructorPerson::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

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

HL 11

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

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

HL 12

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

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

HL 13

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<<(a) ). 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

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

HL 14

Overloading << and >> for user defined classes#include <iostream.h>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;

} ;//Applicationvoid main(void) {

Point p;cout << ”Enter point ”;cin >> p;cout << ”The point is ”<< p;

}//Operation function implementationsPoint::Point(float x0, float y0) {

x = x0; y = y0;}//Friend functionsistream &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;

}

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

HL 15

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

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

HL 16

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

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

HL 17

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

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

HL 18

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

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

HL 19

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 conversion Counter -> 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

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

HL 20

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

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

21

Returning a reference (again)Function can return a value1. 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 valuef1() = 10; //NOT OK left value

Function can return a pointer2. 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 valuef2() = 10; //OK left value

Example where comparison is made between returning a reference and returning a pointer:

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

HL 22

// Class IntelligentArray#include <iostream.h>inline int minimum(int a,int b) { return a<b ? a : b; }

//Header of class intelligent arrayclass 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 programint 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)

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

HL 23

//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)

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

HL 24

Operator Associativity

() [] -> . 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