operator overloading & exception handling tcp1201 oopds 1 lecture 5 1

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

Upload: madeleine-montgomery

Post on 13-Jan-2016

219 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

Operator Overloading & Exception Handling

TCP1201 OOPDS 1

Lecture 5

1

Page 2: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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 write multiple catch blocks and

exception matching

2

Page 3: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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() { return num; } int getDen() { return den; }};

4

Rational multiply (Rational& r1, Rational& r2) {

}

int n = r1.getNum() * r2.getNum();int d = r1.getDen() * r2.getDen();return Rational (n, d);

Page 5: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

• 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; ...

Simple & easy to

understand

Complex

Operator Overloading Advantage

5

Page 6: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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() { cout << num << "/" << den << endl; } int getNum() { return num; } int getDen() { return den; }};Rational operator* (Rational& r1, 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

Non-friend function call methods.

10

Page 11: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

friend Access Privilege

class Rational { friend Rational operator* (Rational&, Rational&); int num, den; ...};Rational operator* (Rational& r1, Rational& r2) { int n = r1.num * r2.num; // Directly access private member. int d = r1.den * r2.den; return Rational (n, d);}

If class A grants a global function (not a method) or another class B (not subclass) a friend access privilege, it means that class A allows the global function or class B to access its private members.This actually violates encapsulation but is generally accepted when doing operator overloading.A friend function is not a method of the class.

11

Page 12: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

Overloading Operator '*' as Friend Function

class Rational { friend Rational operator* (Rational&, Rational&); int num; int den; public: Rational (int num=0, int den=1) : num(num), den(den) {} void print() { cout << num << "/" << den << endl; }};Rational operator* (Rational& r1, 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

Friend function can access private member directly.

12

Page 13: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

istream& operator>> (istream& is, <YourClass>& p) { is >> <code for cin>; return is; // Return existing object instead of new object.}

ostream& operator<< (ostream& os, <YourClass>& p) { os << <code for cout>; return os; // Return existing object instead of new object.}

Overloading Operator '<<' and '>>'• Both insertion operator '<<' and extraction operator

'>>' are binary operators because they need 2 arguments to work.

• They usually have the following pattern (return type and first parameter) regardless of the class you want to overload.

• Your job is to figure out your class and the code for cin or cout.

13

Page 14: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

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 '>>'Overloading '<<' and '>>' allows us to use them on object of user-defined class.

14

ostream& operator<< (ostream& os, Rational& r) { os << r.getNum() << "/" << r.getDen(); return os;}

Page 15: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

Parameters & Return Types

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

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

15

Page 16: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

Increment Operator '++'Is a unary operator that can be used as both a prefix ( ++x) and postfix (x++) operator.How does compiler know whether we are overloading prefix or postfix?

Use our Rational class as example:• The function prototype for overload prefix

operator:

• The function prototype for overloading postfix operator:

• Postfix requires 1 int parameter to differentiate itself from prefix.

Rational operator++(Rational &) // prefix

Rational operator++(Rational &, int) // postfix

16

Page 17: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

class Rational { ...};

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

Overloading Operator '<'Binary operator '<' is for comparing 2 arguments. Hence it should return a Boolean result.

17

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

Page 18: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

#include <algorithm> // sort()...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, Point& p) { os << "(" << p.getX() << ", " << p.getY() << ")"; return os;}

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;

}

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

18

class SortByX { public: bool operator()(const Point& p1, const Point& p2) { return p1.getX() < p2.getX(); }};class SortByY { public: bool operator() (const Point& p1, const Point& p2) { return p1.getY() < p2.getY(); }};

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] << " ";

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

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

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

Page 19: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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.

19

Page 20: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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?

20

Page 21: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

21

Page 22: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

22

try { // Code that may generate exceptions. ...

}

Page 23: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

23

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

Page 24: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

24

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.}// No code here.catch (<Exception type>) { // Thrown exception object must // match caught exception type. ...}

Page 25: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

25

Page 26: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

26

Page 27: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

27

Page 28: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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,

28

Page 29: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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.

29

Page 30: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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.

30

Page 31: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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.

31

Page 32: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

Multiple catch Blocks

32

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 33: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

Multiple catch Blocks

33

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 34: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

Multiple catch Blocks

34

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 35: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

35

Page 36: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

36

Page 37: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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

37

Page 38: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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"; }}

38

Page 39: Operator Overloading & Exception Handling TCP1201 OOPDS 1 Lecture 5 1

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.

39