Lecture05 operator overloading-and_exception_handling

35
Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

Transcript of Lecture05 operator overloading-and_exception_handling

Page 1: Lecture05 operator overloading-and_exception_handling

Operator Overloading & Exception Handling

TCP1201 OOPDS 1

Lecture 5

1

Page 2: Lecture05 operator overloading-and_exception_handling

Learning Objectives

Operator Overloading To understand what is operator overloading To understand the advantage of operator overloading To understand how to overload operators as functionsException handling To understand what is exception To realize the advantages of exception handling To understand the use of try, throw and catch block To understand how exception propagation works To understand how to wrote multiple catch blocks and

exception matching

2

Page 3: Lecture05 operator overloading-and_exception_handling

Operator Overloading• Add capability to an operator via writing a new "special

function" for the same operator but with different data types and combinations of parameters.

• For example, C++'s operator '+':• C++ has built-in support for using operator '+' to add int or double, and also for concatenating string.

• However using operator '+' to add 2 objects of your (user-defined) class is not automatically supported.

• We can write a "function" to make C++ support adding 2 objects of user-defined class. This process is called operator overloading.

3

Page 4: Lecture05 operator overloading-and_exception_handling

Operator Overloading Advantage • It provides a simpler way for doing some

operations on user-defined classes.• Consider the following Rational class which

represents a rational/fraction number, e.g. 1/2, 2/3:

class Rational { int num; // numerator int den; // denominator public: Rational (int num=0, int den=1) : num(num), den(den) {} int getNum() const { return num; } int getDen() const { return den; }};Rational multiply (const Rational& r1, const Rational& r2) { int n = r1.getNum() * r2.getNum(); int d = r1.getDen() * r2.getDen(); return Rational (n, d);}

4

Page 5: Lecture05 operator overloading-and_exception_handling

• To multiply rational numbers e.g. 1/2 * 1/3 * 5/6 = 5/36, we can use the method multiply(), but it looks more complex.

• If we overload multiply operator '*', we can write:

int main() { Rational r1(1,2), r2(1,3), r3(5,6), r4; r4 = multiply(r1, multiply(r2,r3)); ...

int main() { Rational r1(1,2), r2(1,3), r3(5,6), r4; r4 = r1 * r2 * r3; ...

Complex

Simple & easy to

understand

Operator Overloading Advantage

5

Page 6: Lecture05 operator overloading-and_exception_handling

Operators in C++ are divided into 2 categories based on the number of arguments they accept:

• Unary operators accept one argument

• x++, --x, !x, etc.

• Binary operators accept two arguments

• x+y, x-y, x*y, x<y, x=y, etc.

C++ Operators

Page 7: Lecture05 operator overloading-and_exception_handling

Some unary operators can be used as both prefix and postfix operators, e.g. increment ‘++’ and decrement ‘--’ operators.

int a = b = 0;++a; // a = 1b = ++a; // b = 2, a = 2cout << "a is:" << a; // a is 2cout << "b is:" << b; // b is 2

b = a++; // b = 2 // a = 3cout << "a is:" << a; // a is 3cout << "b is:" << b; // b is 2

prefix

postfix

C++ Operators

7

Page 8: Lecture05 operator overloading-and_exception_handling

We overload an operator by writing a special function with the keyword operatorS, where S is an operator symbol (+, -, *, /, !, ++, etc.).

We should overload an operator in a sensible way and meet general expectation, e.g. don't overload '*' operator to perform division.

How to Overload Operators?

returntype operatorS (parameters) { ... return something;}

8

// For our Rational number multiplication.returntype operator* (parameters) { ... return something;}

Page 9: Lecture05 operator overloading-and_exception_handling

The number of parameters depend whether the overloaded operator is unary or binary.• Unary operator has 1 parameter.• Binary operator has 2 parameters.

Since operator '*' is a binary operator hence the number of parameter is 2.

We are multiplying 2 Rational objects and expecting the result is also a Rational object, hence the data type of both parameter and return type should be Rational.

How to Overload Operators?

9

// For our Rational number multiplication.Rational operator* (Rational, Rational) { ... return Rational(); // return a new object.}

Page 10: Lecture05 operator overloading-and_exception_handling

Parameters & Return Types

• For parameters, use references whenever possible (especially when the parameter is a big object).

• Always use pass-by-const reference when the argument will not be modified.

• Always try to follow the spirit of the built-in implementations, e.g. comparison operators (==, !=, >, etc) generally return a bool, so an overloaded version should do the same.

• Operators that return a reference to the original object should return a non-const reference to ensure that the result can be modified in a later operation., e.g. <<, >>, =, etc.

10

Page 11: Lecture05 operator overloading-and_exception_handling

Overloading Operator* as Non-Friend Function

class Rational { int num; int den; public: Rational (int num = 0, int den = 1) : num(num), den(den) {} void print() const { cout << num << "/" << den << endl; } int getNum() const { return num; } int getDen() const { return den; }};Rational operator* (const Rational& r1, const Rational& r2) { int n = r1.getNum() * r2.getNum(); int d = r1.getDen() * r2.getDen(); return Rational (n, d); // Return a new // Rational object}

int main() { Rational r1(1,2), r2(1,3), r3(5,6), r4; r4 = r1 * r2 * r3; r1.print(); r2.print(); r3.print(); r4.print();}

Output:1/21/35/65/36

Binary operator has 2 parameters.

11

Page 12: Lecture05 operator overloading-and_exception_handling

Overloading Operator* as Friend Functionclass Rational { friend Rational operator* (const Rational&, const Rational&); int num; int den; public: Rational (int num=0, int den=1) : num(num), den(den) {} void print() const { cout << num << "/" << den << endl; }};Rational operator* (const Rational& r1, const Rational& r2) { int n = r1.num * r2.num; int d = r1.den * r2.den; return Rational (n, d); // Return a new // Rational object}

int main() { Rational r1(1,2), r2(1,3), r3(5,6), r4; r4 = r1 * r2 * r3; r1.print(); r2.print(); r3.print(); r4.print();}

Output:1/21/35/65/36

Binary operator has 2 parameters.

12

Page 13: Lecture05 operator overloading-and_exception_handling

class Rational { friend istream& operator>> (istream&, Rational&); ...};istream& operator>> (istream& is, Rational& r) { is >> r.num >> r.den; // r.getNum() & r.getDen() won't work. Why? return is;}ostream& operator<< (ostream& os, const Rational& r) { os << r.getNum() << "/" << r.getDen(); return os;}

int main() { Rational r1, r2, r3; cin >> r1 >> r2; r3 = r1 * r2; cout<< r1<<endl << r2<<endl << r3;}

Output:1 2 3 41/23/43/8

Overloading Operator<< and >>Binary operator as function has 2 parameters

13

Page 14: Lecture05 operator overloading-and_exception_handling

The Increment Operator (‘++’)Is a unary operator that can be used as both a prefix ( ++x) and postfix (x++) operator.

If we have overloaded both prefix and postfix versions of the increment operator for class Rational:• A prefix call to operator (++x) will generate a call

to:

• A postfix call to operator (x++) will generate a call to:

Postfix requires 1 int parameter to differentiate itself from prefix.

Rational::operator++() // prefix

Rational::operator++(int) // postfix

14

Page 15: Lecture05 operator overloading-and_exception_handling

class Rational { ...};

bool operator< (const Rational& r1, const Rational& r2) { return r1.getNum() * r2.getDen() < r1.getDen() * r2.getNum();}

int main() { Rational r1(1,2), r2(2,3), r3(1,2); if (r1 < r2) cout << "r1 is smaller than r2\n"; else cout << "r1 is NOT smaller than r2\n"; if (r1 < r3) cout << "r1 is smaller than r3\n"; else cout << "r1 is NOT smaller than r3\n";}

Output:r1 is smaller than r2r1 is NOT smaller than r3

Overloading Operator<Binary operator has 2 parameters.

15

Page 16: Lecture05 operator overloading-and_exception_handling

class Point { int x, y; public: Point (int x = 0, int y = 0) : x(x), y(y) { } int getX() const { return x; } int getY() const { return y; }};ostream& operator<< (ostream& os, const Point& p) { os << "(" << p.getX() << ", " << p.getY() << ")"; return os;}struct SortByX { bool operator() (const Point& p1, const Point& p2) { return p1.getX() < p2.getX(); }};struct SortByY { bool operator() (const Point& p1, const Point& p2) { return p1.getY() < p2.getY(); }};

int main() { Point pts[3] = {Point(3,6), Point(5,4), Point(1,2)}; for (int i=0; i<3; i++) cout << pts[i] << " "; cout << endl; sort (pts, pts+3, SortByX()); for (int i=0; i<3; i++) cout << pts[i] << " "; cout << endl; sort (pts, pts+3, SortByY()); for (int i=0; i<3; i++) cout << pts[i] << " ";}

Output:(3, 6) (5, 4) (1, 2)(1, 2) (3, 6) (5, 4)(1, 2) (5, 4) (3, 6)

Overloading Operator()To sort an array or vector of your class by different attribute at different time.

16

Page 17: Lecture05 operator overloading-and_exception_handling

Exception Handling

When a program is executed, unexpected situations may occur. Such situations are called exceptions.In other word: An exception is a runtime error caused by some abnormal conditions.

Example:• Division by zero• Failure of new operator to obtain a requested amount of

memory

Exception handler is code that handles the exception (runtime error) when it occurs.

17

Page 18: Lecture05 operator overloading-and_exception_handling

Exception Example: Division By Zero

double divide (double x, double y) { return x / y; // divide by 0 if y = 0}int main() { double x, y; cin >> x >> y; cout << "Result = " << divide (x, y); }

How to deal with the error below?

18

Page 19: Lecture05 operator overloading-and_exception_handling

Exception Example: Division By Zero

double divide (double x, double y) { return x / y; // divide by 0 if y = 0}int main() { double x, y; cin >> x >> y; if (y == 0) cout << "Cannot divide by zero\n"; else cout << "Result = " << divide (x, y); }

A solution is shown below. It works but the codes that handles the error mixes with the codes for division, making the codes harder to read (is if for division and else for error handling, or the other way? No direct indication from if/else keywords alone.

19

Page 20: Lecture05 operator overloading-and_exception_handling

C++ implements exception handling using try, throw and catch block.

try block:• Write the code that might generate runtime error within

the try block.

Exception Handling

20

try { // Code that may generate exceptions. ... if (<error condition is true>) throw <Exception object>; ...}catch (<Exception type>) { // Error handling code. ...}

Page 21: Lecture05 operator overloading-and_exception_handling

throw statement:• Use keyword throw in try block to signal that abnormal

condition or error has occurred.• If the throw statement is executed, the C++ runtime will

skip the remaining of the try block, and jump to the catch block to continue execution.

try, throw, and catch blocks

21

try { // Code that may generate exceptions. ... if (<error condition is true>) throw <Exception object>; // Jump to catch block. ... // Will be skipped if throw statement is executed.}catch (<Exception type>) { // Error handling code. ...}

Page 22: Lecture05 operator overloading-and_exception_handling

catch block:• Write the code that catches the thrown exception object

in catch block. This is the exception handler. • Unhandled/Uncaught thrown exception will terminate the

program.

try, throw, and catch blocks

22

try { // Code that may generate exceptions. ... if (<error condition is true>) throw <Exception object>; ...}// No code here.catch (<Exception type>) { // Thrown exception object must // match caught exception type. // Error handling code. ...}

Page 23: Lecture05 operator overloading-and_exception_handling

double divide (double x, double y) { if (y == 0) throw y; return x / y; }int main() { double x, y; cin >> x >> y; try { double result = divide (x, y); cout << "Result = " << result; } catch (double a) { cout << "Cannot divide by zero\n"; }}

If there is an exception, throw it.

Put code that may generate error in try block.

If there is no exception, resume execution.

If there is an exception of type double, catch it.

Example: try, throw, and catch blocks

23

Page 24: Lecture05 operator overloading-and_exception_handling

double divide (double x, double y) { if (y == 0) throw y; return x / y; }int main() { double x, y; cin >> x >> y; try { double result = divide (x, y); cout << "Result = " << result; } catch (double a) { cout << "Cannot divide by zero\n"; }}

Output1:No exception1 2Result = 0.5

Output2:With exception1 0Cannot divide by zero

When an exception is thrown, the codes that appear after the throw statement in the try block is skipped.

Example: try, throw, and catch blocks

24

Page 25: Lecture05 operator overloading-and_exception_handling

double divide (double x, double y) { if (y == 0) throw y; return x / y; }int main() { double x, y; cin >> x >> y; try { double result = divide (x, y); cout << "Result = " << result; } catch (double a) { cout << "Cannot divide by zero\n"; }}

Example: try, throw, and catch blocks

Output1:No exception1 2Result = 0.5

Output2:With exception1 0Cannot divide by zero

The type of the object being thrown must match the type of the parameter in the catch block

25

Page 26: Lecture05 operator overloading-and_exception_handling

double divide (double x, double y) { if (y == 0) throw y; return x / y; }int main() { double x, y; cin >> x >> y; try { double result = divide (x, y); cout << "Result = " << result; } catch (int a) { cout << "Cannot divide by zero\n"; }}

Example: try, throw, and catch blocks

Output1:No exception1 2Result = 0.5

Output2:With exception1 0terminate called after

throwing an instance of 'double'

If the type of object being thrown does not match the type of the parameter in the catch block,

26

Page 27: Lecture05 operator overloading-and_exception_handling

int main() { double x, y; cin >> x >> y; try { if (y == 0) throw y; double result = x / y; cout << "Result = " << result; } catch (double a) { cout << "Cannot divide by zero\n"; }}

Example: try, throw, and catch blocks

Output1:No exception1 2Result = 0.5

Output2:With exception1 0Cannot divide by zero

Note that exception handling does not require a function to work.

27

Page 28: Lecture05 operator overloading-and_exception_handling

Exception Propagation

If the function containing the throw statement does not catch the exception, the exception will be propagated up to the caller of the function until it reaches a try block or the main function.

In the former case, the try/catch block of the caller handles the exception if the exception type matches one of the catch block. Otherwise the exception will be propagated up again.

If the exception reaches the main function and is not handled, the program will be terminated.

28

Page 29: Lecture05 operator overloading-and_exception_handling

Example: Exception Propagationdouble f2(double x, double y) { if (y == 0) throw y; return x / y; }double f1(double x, double y) { return f2(x, y);}double divide (double x, double y) { return f1(x, y);}int main() { ... try { double result = divide (x, y); cout << "Result = " << result; } catch (double a) { ...

Output:With exception1 0Cannot divide by zero

The exception is propagated in the following order: f2(), f1(),divide(),main().

The main() catches and handles the exception.

29

Page 30: Lecture05 operator overloading-and_exception_handling

Sometimes, we might have many different exceptions for a small block of code.

Multiple catch Blocks

30

try { ... if (<Error1>) throw <Object of exception type1>; if (<Error2>) throw <Object of exception type2>; if (<Error3>) throw <Object of exception type3>; ...}catch (<Exception type1>) { // Code that resolves a type1 exception.}catch (<Exception type2>) { // Code that resolves a type2 exception.}catch (<Exception type3>) { // Code that resolves a typeN exception.}

Page 31: Lecture05 operator overloading-and_exception_handling

But, which catch block will be instigated/invoked? Depend on the type of exception object.

The type must match exactly, no implicit conversion will be done by C++. Type double does not match with type int.

Only one catch block will be executed for an exception. The catch block that first matches the exception type would be chosen.

Multiple catch Blocks

31

Page 32: Lecture05 operator overloading-and_exception_handling

int main () { func (1); func (3); func (4);}

Output:Catch int 11Catch double 3.5n is not 1 or 3

void func (int n) { try { if (n == 1) throw 11; // int if (n == 3) throw 3.5; // double cout << "n is not 1 or 3\n"; } catch (double a) { // Won't catch int cout << "Catch double " << a << endl; } catch (int a) { // Match int cout << "Catch int " << a << endl; }}

No implicit conversion of exception type in catch argument

Multiple catch Blocks

32

Page 33: Lecture05 operator overloading-and_exception_handling

Exception Matching

To catch every possible exception type, use ellipsis "…".

Limitations of catch (...):• You can't tell what type of exception occurred.• No argument to reference.• Should always be placed as the last catch block.

try { ... } catch (...) { // catches ALL exception types. ... }

33

Page 34: Lecture05 operator overloading-and_exception_handling

Exception Matchingint main () { func (1); func (2); func (3); func (4);}

Output:Not double nor stringCatch string abcCatch double 3.5n is not 1, 2 or 3

void func (int n) { try { if (n == 1) throw 11; // int if (n == 2) throw string("abc"); if (n == 3) throw 3.5; // double cout << "n is not 1, 2 or 3\n"; } catch (double a) { cout << "Catch double " << a << endl; } catch (string a) { cout << "Catch string " << a << endl; } catch (...) { // all types cout << "Not double nor string\n"; }}

34

Page 35: Lecture05 operator overloading-and_exception_handling

Advantages of Exception Handling

Using try, throw, and catch blocks to handle exception offer the following advantages:1. Provide clarify on the section of codes that

handle the error.2. You may throw an exception in a

function/method, and handle it somewhere else.

35