COMP 345 Advanced Program Design with C 1

  • Slides: 23
Download presentation
COMP 345 - Advanced Program Design with C++ 1 Click to edit Master title

COMP 345 - Advanced Program Design with C++ 1 Click to edit Master title style ADVANCED PROGRAM DESIGN WITH C++ Part 9: Operator overloading Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 2 Operator overloading • Sometimes using

COMP 345 - Advanced Program Design with C++ 2 Operator overloading • Sometimes using methods to implement arithmetic-style operations is not very convenient. • Let us take as an example a Rational class: class Rational { public: Rational(int = 0, int = 1); Rational(const Rational & r); int numerator; int denominator; }; Concordia University // // default constructor copy constructor numerator of fraction denominator of fraction Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 3 Operator overloading • To implement

COMP 345 - Advanced Program Design with C++ 3 Operator overloading • To implement the addition of two Rational objects, the following method could be added: const Rational: : add(const Rational& r) const { int n = numerator * r. denominator + denominator * r. numerator; int d = denominator * r. denominator; return Rational(n, d); } • Such a method would require that we add three Rational numbers in the following awkward manner: Rational Concordia University a(5, 6); b(2, 3); c(1, 2); d = a. add(b. add(c)); Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 4 Operator overloading • Since the

COMP 345 - Advanced Program Design with C++ 4 Operator overloading • Since the addition operation has an arithmetic flavor to it, it would make much more sense if we could write: Rational d = a + b + c; • C++ provides a very convenient way to implement arithmetic-style operators: operator overloading. Rational: : operator+(const Rational& a) const { Rational sum; sum. numerator = a. numerator * denominator + a. denominator * numerator; sum. denominator = a. denominator * denominator; return sum; } Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 5 Operator overloading • Operator overloading

COMP 345 - Advanced Program Design with C++ 5 Operator overloading • Operator overloading allows existing C++ operators to work with user-defined data types. • Limitations: • At least one operand must be a user-defined type, i. e. it is impossible to change the meaning of an operator applied to a basic type such as int. • Restricted to a fixed set of operators. • Cannot change precedence and associativity of operators. • Should not change the meaning of an operator, e. g. operator+ should always do something similar to addition. • Otherwise it would be confusing. Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 6 Operator overloading • Operators that

COMP 345 - Advanced Program Design with C++ 6 Operator overloading • Operators that can be overloaded: + & ++ <= += |= -> Concordia University | -== -= ^= ->* * ~ << != *= <<= new / % ! && >> , > >= /= %= >>= [ ] delete Department of Computer Science and Software Engineering ^ || < = &= ( ) Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 7 Operator overloading • Operators can

COMP 345 - Advanced Program Design with C++ 7 Operator overloading • Operators can be overloaded either as member functions or as free operators. • Unary operators can be members with no parameter or free operators with one parameter: // unary minus member operator const Rational: : operator-() const { Rational minus; minus. numerator = -(this->numerator); minus. denominator = this->denominator; return minus; } // unary minus free operator const Rational operator-(Rational& a) { Rational minus; minus. numerator = -(a. numerator); minus. denominator = a. denominator; return minus; } Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 8 Operator overloading • Binary operators

COMP 345 - Advanced Program Design with C++ 8 Operator overloading • Binary operators can be members with one parameter or free operators with two parameters: // binary operator+ as member operator const Rational: : operator+(const Rational& a) const { Rational sum; sum. numerator = a. numerator * this->denominator + a. denominator * this->numerator; sum. denominator = a. denominator * this->denominator; return sum; } // binary operator+ as free operator const Rational operator+(const Rational& a, const Rational& b) { Rational sum; sum. numerator = a. numerator * b. denominator + a. denominator * b. numerator; sum. denominator = a. denominator * b. denominator; return sum; } Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 9 Operator overloading • The result

COMP 345 - Advanced Program Design with C++ 9 Operator overloading • The result of operators should be a new value returned by the operator, which should be constant, so that the result cannot be the target of an assignment: Rational r 1(1, 2), r 2(2, 3), r 3(0, 1); (r 1 + r 2) = r 3; //should not be allowed! • Parameters should be passed by value or by constant reference, as the operands should not be modified by the operations. const Rational operator+(const Rational& a, const Rational& b) • Member operators should be declared as constant to signify that the operation does not alter the calling object. const Rational: : operator+(const Rational& a) const Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 10 Operator overloading • Increment/decrement operators

COMP 345 - Advanced Program Design with C++ 10 Operator overloading • Increment/decrement operators overloading • Prefix: increments and then returns value // prefix increment operator Rational& Rational: : operator++(){ numerator = numerator + denominator; return *this; } • Postfix: increments but returns original value. Note: postfix operator uses a dummy int parameter // postfix increment operator Rational: : operator++(int){ Rational temp = *this; numerator = numerator + denominator; return temp; } Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 11 Operator overloading • Stream input/output

COMP 345 - Advanced Program Design with C++ 11 Operator overloading • Stream input/output operators overloading. • Must be overloaded as free operators. ostream& operator<<(ostream &output, const Rational &r) { output << r. numerator << "/" << r. denominator; return output; } istream& operator>>(istream &input, Rational &r) { input >> r. numerator >> r. denominator; return input; } • Uses the stream input/output operators to input/output individual values of the data members. • Returns the stream as a reference so that it can then be used sequentially, e. g. : cout << ++a << endl << b++ << endl << b << endl; Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 12 Operator overloading • Can implement

COMP 345 - Advanced Program Design with C++ 12 Operator overloading • Can implement casting operators: Rational: : operator double(){ return numerator / (double)denominator; } • Will be called implicitly by the compiler when necessary, e. g. : Rational b(2, 3); float f = b; //casting operator implicitly called • Or when an explicit cast is made, e. g. : cout << b++ << endl << b << endl << (float)b << endl; • Note that the casting operator has very high precedence: Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 13 Operator overloading • Overloading the

COMP 345 - Advanced Program Design with C++ 13 Operator overloading • Overloading the assignment operator: const Rational& Rational: : operator=(const Rational& r){ numerator = r. numerator; denominator = r. denominator; return *this; } • The assignment operator returns a reference to its result, so that a cascade of assignments is possible. • If an assignment operator is not provided, the compiler will generate one automatically, which will do a member-wise shallow copy. If no data members are pointers, an assignment operator does not need to be explicitly overloaded. • If the class contains pointer members, the assignment operator must be explicitly overloaded, or else it will be only copying the pointer values. Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 14 Operator overloading • Let us

COMP 345 - Advanced Program Design with C++ 14 Operator overloading • Let us imagine that numerator and denominator are int*, then we may have the following assignment operator: const Rational& Rational: : operator=(const Rational& r){ numerator = new int (r. numerator); denominator = new int (r. denominator); return *this; } • This would in fact create a memory leak, as the memory previously reserved to numerator and denominator has not been deleted. So what we need seems to be the following: const Rational& Rational: : operator=(const Rational& r){ delete numerator; delete denominator; numerator = new int (r. numerator); denominator = new int (r. denominator); return *this; } Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 15 Operator overloading • But then

COMP 345 - Advanced Program Design with C++ 15 Operator overloading • But then let us imagine that we do a self-assignment, e. g. : Rational a(5, 6); a = a; • This would in fact lead us to delete the memory allocated to the pointers first, and then try to copy their value, which would fail. So we also need to do a self-assignment check: const Rational& Rational: : operator=(const Rational& r){ if (&r != this){ delete numerator; delete denominator; numerator = new int (r. numerator); denominator = new int (r. denominator); } return *this; } Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 16 Operator overloading • There are

COMP 345 - Advanced Program Design with C++ 16 Operator overloading • There are some problems with the use of member operators. • For example, what if we want to do the following (legitimate) operation: Rational a(5, 6); Rational c = a + 25; • This requires the operator+ to be overloaded in the Rational class again, but this time taking a parameter of type int. • However, this does not entirely solve the problem, as we might want to do: Rational a(5, 6); Rational c = 25 + a; • In this case, what would be needed is to overload the operator+ for int that would take a parameter of type Rational. Unfortunately, we cannot do that. Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 17 Operator overloading • So the

COMP 345 - Advanced Program Design with C++ 17 Operator overloading • So the best solution is to define our operators as free operators. • This way, we can overload them with any operand type we need, as both of the operands are explicitly mentioned. // operator+ as free operator const Rational operator+(const Rational& a, const Rational& b) { Rational sum; sum. numerator = a. numerator * b. denominator + a. denominator * b. numerator; sum. denominator = a. denominator * b. denominator; return sum; } // operator+ as free operator const Rational operator+(const Rational& a, const int& b_int) { Rational sum, b(b_int, 1); sum. numerator = a. numerator * b. denominator + a. denominator * b. numerator; sum. denominator = a. denominator * b. denominator; return sum; } // operator+ as free operator const Rational operator+(const int& a_int, const Rational& b) { Rational sum, a(a_int, 1); sum. numerator = a. numerator * b. denominator + a. denominator * b. numerator; sum. denominator = a. denominator * b. denominator; return sum; } Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 18 Operator overloading • Let us

COMP 345 - Advanced Program Design with C++ 18 Operator overloading • Let us redefine our class so that the data members are now private, which is better practice: class Rational { public: Rational(int = 0, int = 1); Rational(const Rational & r); private: int numerator; int denominator; }; // default constructor // copy constructor // numerator of fraction // denominator of fraction • Then our three overloaded free operator+ don’t work anymore, as they cannot access the private members of the class Rational. Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 19 Operator overloading • A solution

COMP 345 - Advanced Program Design with C++ 19 Operator overloading • A solution would be to provide accessors for numerator and denominator, which is something that we may not want to do, as it would expose the data to the exterior. • A better solution is to declare the free operators as friends to the Rational class. In fact, this is one of the most common use of friends. class Rational { friend const Rational operator+(const Rational&, const Rational&); friend const Rational operator+(const Rational&, const int&); friend const Rational operator+(const int&, const Rational&); public: Rational(int = 0, int = 1); // default constructor Rational(const Rational & r); // copy constructor private: int numerator; // numerator of fraction int denominator; // denominator of fraction }; Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 20 Operator overloading • As member

COMP 345 - Advanced Program Design with C++ 20 Operator overloading • As member • Defined as a member of the class. • May use data members, as the operator is a member of the class. • Operator belongs to the class (as opposed to non-member). • Best from the point of view of OO principles, but… • The calling object is the left operand of the operator, so thus this suffers from lack of type conversion of the left operand if it is a basic type. • As non-member non-friend • Defined as external to the class. • Not related nor mentioned in the class, except by its operands type. • Implementation code cannot refer to private members of its operands, thus necessitating accessors, which might not be desirable. • Declares all operands and no calling object. • May accommodate different types for all its operands. • As friend • Defined as external to the class, but introduced inside the class as a friend • At least, it is mentioned in the class declaration, though it is not a member. • As a friend, it may use private data members, thus does not necessitate accessors. • Has two declared operands and no calling object. • May have type conversion for both of its operands. Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 21 Overloading operator[] • The square

COMP 345 - Advanced Program Design with C++ 21 Overloading operator[] • The square brackets operator can also be overloaded, typically to access an indexable element of the object. class Array { private: int* ptr; int size; public: Array(int*, int); int& operator[](int); }; int& Array: : operator[](int index){ if (index >= size) { cout << "Array index out of bound, exiting"; exit(0); } return ptr[index]; } Array: : Array(int* p = NULL, int s = 0){ size = s; ptr = NULL; if (s != 0) { ptr = new int[s]; for (int i = 0; i < s; i++) ptr[i] = p[i]; } } int main(){ int a[] = { 1, 2, 4, 5 }; Array arr 1(a, 4); arr 1[2] = 6; arr 1[8] = 6; return 0; } Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 22 Overloading new and delete •

COMP 345 - Advanced Program Design with C++ 22 Overloading new and delete • The new and delete operators can also be overloaded for a class: class Test { int x; public: void* operator new(size_t size); void operator delete(void*); Test(int i) { x = i; cout << "Constructor called n"; } ~Test() { cout << "Destructor called n"; } }; void* Test: : operator new(size_t size){ void* storage = malloc(size); cout << "new called n"; return storage; } void Test: : operator delete(void* p){ cout << "delete called n"; free(p); } int main(){ Test* m = new Test(5); delete m; return 0; } Concordia University Execution: new called Constructor called Destructor called delete called Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020

COMP 345 - Advanced Program Design with C++ 23 References • Michigan Technical University.

COMP 345 - Advanced Program Design with C++ 23 References • Michigan Technical University. Operator overloading. • Paul Deitel, Harvey Deitel. C++ How To Program. Prentice Hall, 2011. ISBN-13: 978 -0132662369 • Y. Daniel Liang. Introduction to Programming with C++. Chapter 14. Prentice Hall, 2014. ISBN-13: 978 -0133252811 Concordia University Department of Computer Science and Software Engineering Joey Paquet, 2007 -2020