OPERATOR OVERLOADING CS 3370 C Chapter 14 Operator

  • Slides: 32
Download presentation
OPERATOR OVERLOADING CS 3370 – C++ Chapter 14

OPERATOR OVERLOADING CS 3370 – C++ Chapter 14

Operator Overloading More than just Syntactic Sugar • Binary operators • Unary operators •

Operator Overloading More than just Syntactic Sugar • Binary operators • Unary operators • Conversion Operators • Proxy Classes (simulating a reference) • bitset example • Special operators • Indexing • Pre-post increment/decrement • Function call

Complex Numbers A Canonical Example of a Value Type • Complex numbers are pairs

Complex Numbers A Canonical Example of a Value Type • Complex numbers are pairs of real numbers • (real, imaginary), e. g. , (2, 3), (1. 04, -12. 4) • like points in the x-y plane • (a, b) + (c, d) = (a+c, b+d) • applications in Electrical Engineering • Compare function-style operations to using operator functions…

A complex Class #include <iostream> class complex { double real; double imag; public: complex(double

A complex Class #include <iostream> class complex { double real; double imag; public: complex(double real=0, double imag=0) { this->real = real; this->imag = imag; } complex add(const complex& c) const { complex result; result. real = this->real + c. real; result. imag = this->imag + c. imag; return result; } void display(std: : ostream& os) const { os << '(' << real << ', ' << imag << ')'; } };

Using the complex Class using namespace std; int main() { complex z(1, 2), w(3,

Using the complex Class using namespace std; int main() { complex z(1, 2), w(3, 4); complex a = z. add(w); a. display(cout); } (4, 6) // want a = z+w

Operator Functions • operator keyword together with an operator token • operator+ • operator

Operator Functions • operator keyword together with an operator token • operator+ • operator • etc. • Can overload all operators except: • : : • . * • ? :

complex with operator+ A member-function implementation class complex { //. . . public: //.

complex with operator+ A member-function implementation class complex { //. . . public: //. . . complex operator+(const complex& c) const { complex result; result. real = this->real + c. real; result. imag = this->imag + c. imag; return result; } //. . . }; int main() { complex z(1, 2), w(3, 4); complex a = z + w; a. display(cout); } // z. operator+(w)

Rules • operator+ is the function name • you can also invoke it directly

Rules • operator+ is the function name • you can also invoke it directly as z. operator+(w) • a binary function, of course • Normal precedence rules apply • Can be either a global or member function • If a member, this is the left operand • If global, at least one argument must be user-defined

Conversion Constructors and Operator Overloading • Single-arg constructors provide implicit conversions • For example

Conversion Constructors and Operator Overloading • Single-arg constructors provide implicit conversions • For example T: : T(int) • Can use 2 initialization syntaxes: • T t(1); • T t = 1; • Provide implicit conversions • t + 1 becomes t + T(1) • Can disable with the explicit keyword

complex Conversion class complex { public: complex(double real = 0, double imag = 0)

complex Conversion class complex { public: complex(double real = 0, double imag = 0) { this->real = real; this->imag = imag; } // … }; int main() { complex w(3, 4); complex a = w + 1; a. display(cout); } (4, 4) // w + (1, 0)

Member vs. Non-member Operators • The expression 1 + w won’t compile. • Left-operand

Member vs. Non-member Operators • The expression 1 + w won’t compile. • Left-operand must be a class object to match a member operator • A non-member operator will apply an implicit conversion to either operand via a single-arg constructor, if available • In general, binary operators should be non-members • For math-like operators, anyway • In general, unary operators should be members

Non-member complex Operators class complex { public: complex(double real = 0, double imag =

Non-member complex Operators class complex { public: complex(double real = 0, double imag = 0) { this->real = real; this->imag = imag; } double get. Real() const {return real; } double get. Imag() const {return imag; } //. . . }; complex operator+(const complex& c 1, const complex& c 2) { double real = c 1. get. Real() + c 2. get. Real(); double imag = c 1. get. Imag() + c 2. get. Imag(); complex result(real, imag); return result; }

A Unary complex Operator class complex { public: complex operator-() const { return complex(-real,

A Unary complex Operator class complex { public: complex operator-() const { return complex(-real, -imag); } // … }; int main() { complex w(3, 4); complex a = -w; a. display(cout); } (-3, -4)

Stream Operators • operator<< and operator>> • Must be global • because the left

Stream Operators • operator<< and operator>> • Must be global • because the left operand is a stream! • Stream is passed by reference • for efficiency • it disallows copy anyway • Should also return the stream • to support chaining insertions and extractions

complex Stream Output ostream& operator<<(ostream& os, const complex& c) { os << '(' <<

complex Stream Output ostream& operator<<(ostream& os, const complex& c) { os << '(' << c. get. Real() << ', ’ << c. get. Imag() << ')'; return os; } int main() { complex w(3, 4); complex a = -w; cout << a << endl; } (-3, -4)

A “Complete” complex • Would provide all pertinent operators • including assignment ops such

A “Complete” complex • Would provide all pertinent operators • including assignment ops such as +=, -=, etc. • assignment ops must be members • typically implement op in terms of op= • Provide stream insertion and extraction • Global functions are usually class friends • remember the “Interface Principle”

Implementing op+ and op+= Typical implementation pattern • Develop operator+= first • it should

Implementing op+ and op+= Typical implementation pattern • Develop operator+= first • it should return a reference to this • Write operator+ in terms of operator+=: T operator+(T t 1, const T& t 2) { return t 1 += t 2; } • (Note: t 1 is passed by value)

operator[] • A Unary Operator • And takes an integer index argument, of course

operator[] • A Unary Operator • And takes an integer index argument, of course • Must be a member • For “array-like” things • vectors, strings • Usually should provide two versions: • a version for const objects • a version for non-const objects • other operators may require this pattern

A “Safe” Array class Also a Useless class : -) class Index { enum

A “Safe” Array class Also a Useless class : -) class Index { enum {N = 100}; int data[N]; int size; public: Index(int n) { if (n > N) throw "dimension error"; for (int i = 0; i < n; ++i) data[i] = i; size = n; } int get. Size() const {return size; } int& operator[](int i) { if (i < 0 || i >= size) throw "index error"; return data[i]; } };

Using Index #include <iostream> using namespace std; int main() { Index a(10); for (int

Using Index #include <iostream> using namespace std; int main() { Index a(10); for (int i = 0; i < a. get. Size(); ++i) cout << a[i] << ' '; cout << endl; a[5] = 99; cout << a[5] << endl; cout << a[10] << endl; } 0123456789 99 abnormal program termination

Using a const Index #include <iostream> using namespace std; int main() { const Index

Using a const Index #include <iostream> using namespace std; int main() { const Index a(10); // a const Index for (int i = 0; i < a. get. Size(); ++i) cout << a[i] << ' '; // COMPILE ERROR! cout << endl; }

Supporting a const Index That pesky Reference Return Problem again! class Index { //.

Supporting a const Index That pesky Reference Return Problem again! class Index { //. . . int& operator[](int i) { if (i < 0 || i >= size) throw "index error"; return data[i]; } }; int operator[](int i) const // A const version { if (i < 0 || i >= size) throw "index error"; return data[i]; }

operator[][] • Doesn’t exist! • Just like multi-dimensional arrays don’t exist • Simulate it

operator[][] • Doesn’t exist! • Just like multi-dimensional arrays don’t exist • Simulate it • Using the “array of arrays” paradigm • a[i][j] == (a. operator[](i)). operator[](j) • The outer operator[] should return something that has another operator[] to invoke • Example: op 2 db. cpp

Conversion Operators • The complement to single-arg constructors • Provide implicit conversions to another

Conversion Operators • The complement to single-arg constructors • Provide implicit conversions to another type • Member function with the signature: operator T() const;

Index-to-double Conversion class Index { //. . . operator double() const { double sum

Index-to-double Conversion class Index { //. . . operator double() const { double sum = data[0]; for (int i = 1; i < size; ++i) sum += data[i]; return sum / size; } }; int main() { const Index a(10); double x = a + 1; cout << x << endl; } 5. 5

Warning! • Consider the expression w + 1 where w is complex. • Design

Warning! • Consider the expression w + 1 where w is complex. • Design heuristic: Don’t allow types to be implicitly converted to each other • You can disable implicit conversions via constructors and conversion operators via the explicit keyword

lvalue vs. rvalue Detection Idiom Used in Program 4 (see returnref. cpp) • For

lvalue vs. rvalue Detection Idiom Used in Program 4 (see returnref. cpp) • For operator[ ] • Because bits are not individually addressable • Setting and resetting a bit are very different operations • Uses a Proxy class • Returned by bitset<N>: : operator[] • Stores the bit position and a reference to the bitset • Overloads operator=(bool) • Converts implicitly to bool

REVIEW PROGRAM 4 SPEC

REVIEW PROGRAM 4 SPEC

Other Operators • -> (for “smart pointers”) • we covered this already • ++,

Other Operators • -> (for “smart pointers”) • we covered this already • ++, - • Both pre and post versions • () • “function-call” operator • We’ll see more of this when we cover algorithms

Overloading ++ and - • Must distinguish between pre and post • Post versions

Overloading ++ and - • Must distinguish between pre and post • Post versions take an extraneous int argument • The post versions must save current value • That’s why the pre versions are more efficient • They should also return a const object • To disallow x++++ • Illegal, modifies temporary • Examples: Pre. Post. cpp, Safe. Array. Ptr. cpp

Overloading operator( ) • The “Function Call” Operator • Constitutes a Function Object •

Overloading operator( ) • The “Function Call” Operator • Constitutes a Function Object • An object that behaves like a function • If class T: : operator( ) exists: • Then t( ) acts like a function call • Example: find. Greater. cpp, find. Greater 2. cpp

Operator Overloading Checkpoint • When should you make an operator function a nonmember? •

Operator Overloading Checkpoint • When should you make an operator function a nonmember? • Why should you provide two versions of operator[]? • What side effect ensues from defining a constructor that be can called with a single argument? • When should you use a conversion operator? • When shouldn't you use a conversion operator?