Chapter 11 Operator Overloading 1 Function Overloading P

  • Slides: 24
Download presentation
Chapter 11 Operator Overloading 1

Chapter 11 Operator Overloading 1

Function Overloading (P. 266) • int square( int x ); • double square( double

Function Overloading (P. 266) • int square( int x ); • double square( double x ); L. 5 Several functions of the same name that perform similar tasks, but on different data types. • int max( int x , int y ); • int max( int x , int y , int z ); 2

11. 3 Operator Overloading (P. 488) • int a, b, c; • string x,

11. 3 Operator Overloading (P. 488) • int a, b, c; • string x, y, z; • a = b + c; // integer addition • x = y + z; // string catenation 3

11. 4 Overloading Binary Operators (P. 489) • Let’s consider the class Rational: class

11. 4 Overloading Binary Operators (P. 489) • Let’s consider the class Rational: class Rational { public: Rational(int n, int d): numerator(n), denominator(d) {}; int numerator; int denominator; }; • How do we perform addition on two rational numbers? 4

Addition of Rational Numbers • Rational a, b, c; • a. denominator = b.

Addition of Rational Numbers • Rational a, b, c; • a. denominator = b. denominator * c. denominator; • a. numerator = b. numerator * c. denominator + b. denominator * c. numerator; • cout << a. numerator << '/' << a. denominator << endl; 5

Member Functions add() and print() Rational add(const Rational& c) const { Rational result(0, 1);

Member Functions add() and print() Rational add(const Rational& c) const { Rational result(0, 1); result. denominator = denominator * c. denominator; result. numerator = numerator * c. denominator + denominator * c. numerator; return result; } void print() const { cout << numerator << '/' << denominator << endl; } a = b. add(c); a. print(); 6

operator+() ★★★ In OOP, you can define the operator + by yourself. Rational operator+(const

operator+() ★★★ In OOP, you can define the operator + by yourself. Rational operator+(const Rational& b) const { Rational result(0, 1); result. denominator = denominator * b. denominator; result. numerator = numerator * b. denominator + denominator * b. numerator; return result; } Now C++ knows how to perform a = b + c; 7

operator+() and add() Rational add(const Rational& b) const { Rational result(0, 1); result. denominator

operator+() and add() Rational add(const Rational& b) const { Rational result(0, 1); result. denominator = denominator * b. denominator; result. numerator = numerator * b. denominator + denominator * b. numerator; return result; } Rational operator+(const Rational& b) const { return this->add(b); } You may re-use the member function add(). 8

Exercise: Operators - * / • Overload the subtraction, multiplication and division operators of

Exercise: Operators - * / • Overload the subtraction, multiplication and division operators of class Rational. (P. 530 Ex 11. 10) 9

11. 5 Overloading the Binary Stream Insertion (<<) friend ostream& operator<<( ostream& output, const

11. 5 Overloading the Binary Stream Insertion (<<) friend ostream& operator<<( ostream& output, const Rational& b ) { output << b. numerator << '/' << b. denominator; return output; } a. print(); cout << " = "; b. print(); cout << " + "; c. print(); cout << endl; cout << a << " = " << b << " + " << c << endl; 10

Stream Insertion Operator (<<) • Consider the one we defined for Rational: friend ostream&

Stream Insertion Operator (<<) • Consider the one we defined for Rational: friend ostream& operator<<(ostream& output, const Rational& b) { output << b. numerator; if (b. denominator > 1) output << '/' << b. denominator; return output; } • Actually, it has some alignment problem: 11

-a * -a 11. 6 Overloading Unary Operators (P. 494) Rational operator~(void) const {

-a * -a 11. 6 Overloading Unary Operators (P. 494) Rational operator~(void) const { return Rational(denominator, numerator); } Rational operator+(void) const { return Rational(numerator+1, denominator); } Rational a(1, 3); cout << a << endl; cout << ~a << endl; cout << +a << endl; # 1/3 # 3/1 # 2/3 13

11. 7 Overloading the Unary Prefix and Postfix (++ and --) Operators (P. 495)

11. 7 Overloading the Unary Prefix and Postfix (++ and --) Operators (P. 495) • The prefix versions are overloaded exactly as any other prefix unary operator would be: Rational& operator++(void) { ++numerator; return *this; } // prefix version • The post-incrementing expression a++ is interpreted as a. operator++(0), so the corresponding member function should be Rational operator++(int) { // postfix version return Rational(numerator++, denominator); } 14

Errara 11. 8 Case Study: A Date Class • P. 498 L. 59 Q:

Errara 11. 8 Case Study: A Date Class • P. 498 L. 59 Q: I understand that we must return a reference to enable cascaded memberfunction calls (P. 465), but why const? • A: The effect of const qualifier is to disallow the returned object to be modified. • In the example in Fig 11. 8, you see no difference for specifying const or not. • However, if you consider an expression (d 3 += 3) += 3, the const qualifier will disallow this expression and the compiler will issue an error. • The parenthesis cannot be omitted, because operator+= is right-associative. • This is certainly a mistake by the author. For other data types (such as int), (n += 3) += 3 is allowed by C++. • Therefore, the const qualifier should be removed from Date. cpp • Also in Date. h (P. 497 L. 17) 15

11. 12 Overloading the Conversion Operator (P. 515) • Conversion operator • Also known

11. 12 Overloading the Conversion Operator (P. 515) • Conversion operator • Also known as “cast operator” • Used to convert an object of one class to an object of another class (usually a fundamental data type) (P. 516 L. 10) • As other overloading operators, this is achieved by defining a member function: • CLASS: : operator Other. Class() const; • CLASS: : operator int() const; • CLASS: : operator char*() const; • With this cast operator (char*), you can use cout without overloading the operator<<. (L. -7) 16

Cast Operator for Class Rational You need not specify the return type. The return

Cast Operator for Class Rational You need not specify the return type. The return type is “char*”! If you don’t know ostringstream (Ch 18, P. 746), try this: operator char*() const { ostringstream oss; if (denominator > 1) oss << numerator << '/' << denominator; operator char*() const { // Conversion operator (a. k. a. cast operator) string s = to_string(numerator) + "/" + to_string(denominator); char* p = new char[s. length() + 1]; strcpy(p, s. c_str()); // g++ (GCC) 4. 8. 2 does not support to_string yet. return p; } else oss << numerator; string s = oss. str(); char* p = new char[s. length() + 1]; strcpy(p, s. c_str()); 1/3 11/3 1/33 return p; }; 17

11. 13 explicit Constructors (P. 517) • Many students ask the “explicit” keyword before

11. 13 explicit Constructors (P. 517) • Many students ask the “explicit” keyword before the constructor. • Suppose we have a class: class Rational { public: int numerator; int denominator; Rational(int n=0, int d=1): numerator(n), denominator(d) {} bool operator==(const Rational& b) { return numerator * b. denominator == denominator * b. numerator; } }; Predict the output of the following code: Rational a(1, 3); Rational b(2, 6); Rational c(2, 1); Rational d(6, 2); int n = 2; cout << std: : boolalpha; cout << (a == b) << endl; cout << (c == n) << endl; 18

C++ Compiler can detect this for you • If a class has a constructor

C++ Compiler can detect this for you • If a class has a constructor which can be called with a single argument, then this constructor becomes conversion constructor which automatically converts the single argument to that class. • If this is not what you want, add a keyword explicit before the constructor. • explicit Rational(int n=0, int d=1): numerator(n), denominator(d) {} • The compiler will detect this error: • no match for ‘operator==’ (operand types are ‘Rational’ and ‘int’) • cout << (c == n) << endl; 19

11. 9 Dynamic Memory Management (P. 501) • In C++, dynamic memory management is

11. 9 Dynamic Memory Management (P. 501) • In C++, dynamic memory management is performed with operators new and delete. • In older C language, it is performed with functions malloc() and free(). • Rational* p = new Rational(1, 4); • Rational* p = new Rational; • If no initial value is specified, the default constructor will be called. • delete p; p = NULL; 20

11. 9 Dynamic Memory Management (P. 501) • Review Section 14. 1. 8 Stack

11. 9 Dynamic Memory Management (P. 501) • Review Section 14. 1. 8 Stack vs Heap Allocation in last semester. Heap Memory space for 1. static variables 2. dynamically allocated by the new operator Code Stack Storing 1. local variables 2. parameters passed (by value) to the function 21

Heap vs. Stack Allocation • http: //ipv 6. ncnu. org/Lang/C++/heap_vs_stack. cpp heap f 2()

Heap vs. Stack Allocation • http: //ipv 6. ncnu. org/Lang/C++/heap_vs_stack. cpp heap f 2() main() f 1() f 2() main() 22

Dynamically Allocating Arrays with new [] • int* pa = new int[10]; • Rational*

Dynamically Allocating Arrays with new [] • int* pa = new int[10]; • Rational* pr = new Rational[10]; • When allocating an array of objects dynamically, each object is initialized by its default constructor. • For fundamental types, the value will be 0 or the equivalent of 0. • e. g. , chars are initialized to the null character • delete [] pa; • delete [] pr; • This statement first calls the destructor for each object in the array, • then deallocates the memory (returning the memory to the heap). 23

11. 14 Overloading the function call operator () (P. 519) • Callable objects #include

11. 14 Overloading the function call operator () (P. 519) • Callable objects #include <iostream> class Int { public: int value; Int(int v=0): value(v) {} int operator()(int n) { return value*n; } }; int main() { Int f(3); std: : cout << f(4) << std: : endl; return 0; } 24

#include <iostream> f vs. f(4) using std: : ostream; class Int { public: int

#include <iostream> f vs. f(4) using std: : ostream; class Int { public: int value; Int(int v=0): value(v) {} int operator()(int n) { return value*n; } friend ostream& operator<<( ostream& output, const Int& b ) { output << b. value; return output; } }; int main() { Int f(3); std: : cout << f << std: : endl; std: : cout << f(4) << std: : endl; return 0; } 25