1 / 56

Software Design and C++ Programming

Software Design and C++ Programming. Lecture 4 Operator Overloading and Streamed I/O. Contents. Introduction Operators in C++ Operator overload functions Member function v friend function overloading Converting between types Overloading the assignment operator

quito
Download Presentation

Software Design and C++ Programming

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Software Design and C++ Programming Lecture 4 Operator Overloading and Streamed I/O

  2. Contents • Introduction • Operators in C++ • Operator overload functions • Member function v friend function overloading • Converting between types • Overloading the assignment operator • Overloading the array subscripting operator • Example – A Date class • Introduction to streams • The ostream and istream classes • User defined input/ouput • Other features of streams

  3. Introduction • Operator overloading is a powerful feature of C++ • It provides programmers with a concise notation for manipulating user defined objects • It is simple to implement given a few basic rules • It is also used in providing input/output capabilities via the Stream classes as we shall see in the next lecture

  4. Operators in C++ • C++ has a rich collection of operators most of which are common to other programming languages • It is the standard arithmetic and logical operators • + - * / % & ! >> << || && == etc • It has thearray indexing and function evaluation operators • [] () • It has the assignment operators • = += -= *= /= &= |= etc

  5. It has the auto increment and decrement operators • ++ -- • It has the pointer de-referencing and address of operators • * & • It has the memory management operators • new delete new[] delete[]

  6. We can divide up the set of operators into unary and binary operators • A unary operator has one operand • Examples if (!x) {..} // unary operator !, operand x x++; // post-fix operator ++, operand x --x; / / pre-fix operator --, operand x int y=a[5]; // operator [], operand a

  7. A binary operator has two operands • Examples int z=x+y; // binary operator +, operands x and y bool z=x&&y; // binary operator && operands x and y x+=y; // binary operator +=, operands x and y

  8. Operator overload functions • In order to overload and operator op, a function operator op must be defined • operator+ to overload + • operator+= to overload += • operator[] to overload [] • etc • We can either provide member functions of a class or external functions (possibly friend functions of a class)

  9. Restrictions on operator overloading • The precedence of an operator cannot be changed • The arity of an operator cannot be changed • We cannot redefine a binary operator to be unary or vice verse • The associativity of an operator cannot change • Whether it applies left to right or right to left

  10. Example • We can define a complex class to represent a complex number class complex { private : double re, im; public : complex(double r,double i) { re = r; im = i;} friend complex operator +(complex, complex); friend complex operator -(complex); };

  11. We have included 2 friend functions for overloading the binary + operator and unary - operator complex operator +(complex a,complex b) { return complex(a.re + b.re , a.im + b.im); } complex operator -(complex a) { return complex(-a.re, -a.im); }

  12. We can also implement the overload functions as member functions • In this case, this replaces one of the arguments class complex { private : double re, im; public : complex(double r,double i) { re = r; im = i;} complex operator +(complex); complex operator -(); };

  13. complex complex::operator +(complex a) { return complex(re + a.re , im + a.im); } complex complex::operator -() { return complex(-re, -im); }

  14. The class can now be used as follows void main() { complex z1(3.0,2.0),z2(3.0,-5.0),z3,z4; z3 = z1 + z2; // overload operator+ z4=-z3; // overload operator- }

  15. z1 + z2 is implemented as operator+(z1,z2) when a global function overload is used and z1.operator+(z2) when a member function overload is used • -z3 is implemented as operator-(z3) when a global function overload is used and z3.operator-() when a member function overload is used

  16. Converting between types • It is often necessary to convert data of one type into data of another type • This is done implicitly by the compiler when we, for example, add an integer variable to a floating point variable • However, for user defined types, the compiler cannot know in advance how to do these conversions • Two mechanisms exist in C++ to convert data types • Conversion constructor • Conversion operator

  17. Conversion constructor • Suppose we wanted to write the following simple piece of code void main() { complex z1,z2; z1 = z2 + 3.0; }

  18. We could write an operator overload function operator+(complex,double) • But we would need to repeat this for every other operator (eg. -,*,/) • The solution is to write a complex->double conversion constructor complex :: complex(double d) { re = d; im = 0; }

  19. z2+3.0 is now interpreted as : operator+(z2,complex(3.0)) • This will cause problems if we have implemented operator+() as a member function • z2+3.0 must be interpreted in the same way as 3.0+z2 (addition is commutative) • Member function implementation of z2+3.0 is z2.operator(complex(3.0)) • There is no equivalent implementation of 3.0+z2

  20. Conversion operator • This is used to to convert an object of one class into an object of another class or into a built in type (int, float, char etc) • Sometimes called a cast operator as it essentially overloads the cast () operation • Declared as a member function of some class, operator X() specifies how to convert an object of this class into an object of type X

  21. As a (silly) example, we could define a member function double() to convert a complex to a double by taking the real part class complex { private : double re, im; public : complex(double r,double i) { re = r; im = i;} operator double() {return re;} };

  22. If we then cast a complex to a double, the conversion operator is called void main() { complex z1(1,0,2.0); double x=(double) z1; // calls double(), x=1.0 }

  23. Overloading the assignment operator • A powerful feature of operator overloading is overloading the assignment operator = • This allows us to specify the action of the statement a=b for two objects a and b of some class • For our simple complex class, implementing operator= is not necessary • z1=z2 causes a default member-wise assignment to be implemented • z1.re=z2.re, z1.im=z2.im

  24. We can see how assignment overload becomes important by considering a simple String class • Memory for the string is allocated dynamically in the constructor

  25. class String { private: char *p; // pointer to string data int size; // length of string public: String (int sz) { p = new char[size = sz];} ~String( ); int getSize( ) { return size; } char* getp( ) { return p;} };

  26. Without an overloaded assignment operator, assignment involves member-wise copy which doesn’t transfer the data • It just copies the pointers main( ) { string s1(3); // string of 3 chars string s2(3); // string of 3 chars s1 = s2; // are you sure? }

  27. “a”, “b”, “c”, “a”, “b”, “c”, Before assignment s1.p s2.p “d”, “e”, “f”, After assignment s1.p “d”, “e”, “f”, s2.p

  28. An assignment overload operatordefined asString& String::operator = (String& a) uses strcpy() to ensure data is copied String& String::operator = (String& a) { if (this != &a) // avoid s = s assignment { delete p; p = new char[size = a.getSize( )]; strcpy(p,a.getp( )); // copy *a.p into *p } return *this; }

  29. “a”, “b”, “c”, “d”, “e”, “f”, Before assignment s1.p s2.p “d”, “e”, “f”, After assignment s1.p s2.p “d”, “e”, “f”,

  30. Why does operator=(String) return a String reference? • Assignment as a right to left associativity • A statement like x=y=z is implemented as (x=(y=z)) • The result of y=z is a reference to object y • This then appears in the x=y assignment • This ‘trick’ is also extensively used in streamed i/o

  31. Overloading the array subscripting operator • The [] operator normally used for array access can be overloaded • It enables us to design a safe array class where attempts to access beyond the bounds of the array are flagged

  32. class SafeArray { private: int* data; int numPoints; public: SafeArray(int); int& operator[](int); };

  33. int& SafeArray::operator[](int index) { if ((index<0)||(index>=numPoints)) { printf( “Index array out of range ”); exit(1); // exit program } return data[index]; }

  34. Why does operator[] return a reference? • Allows array values to be set as well as accessed using the overload function • In other words, the returned value can be used as an lvalue void main() { SafeArray sa(10); // 10 point safe array int j=sa[3]; // Access value sa[5]=5; // Set value int k=sa[10]; // Out of range! }

  35. Example. A Date class • We can design a Date class which uses overloaded ++ operator functions to add 1 to the day • Program statements such as date++ and ++date will then increment the date • We have to think carefully about the differences between the post and pre-increment overload functions

  36. class Date { private: int day,month,year; void helpIncrement(); static const int days[]; public: Date(int d, int m, int y) {day=d;month=m;year=y;} Date& operator++(); // pre-increment Date operator++(int); // post-increment int endOfMonth(int) const; int leapYear(int) const; };

  37. days[] is a convenience array for storing the days per month • helpIncrement() is a convenience function for helping to increment the day • endOfMonth() uses the days[] array to determine if a given day is the last in the month (taking into account leap years) const int Date::days[]= {0,31,28,31,30,31,30,31,31,30,31,30,31};

  38. void Date::helpIncrement() { if (!endOfMonth(day)) ++day; else if (month<12) { ++month; day=1; } else { ++year; month=day=1; } }

  39. Date& Date::operator++() { // Pre-increment operator helpIncrement(); return *this; // reference returned } Date Date::operator++(int) { //Post increment operator Date temp=*this; // current object state helpIncrement(); return temp; // can’t return reference }

  40. The pre-increment operator overload function returns a reference so that pre-incremented Date objects can be used as lvalues (Date ++d=….) • We can’t do this for the post increment overload function as we can’t return a reference to a temporary local variable • In any case syntax such as Date d++=… is not allowed

  41. Introduction to streams • C++ provides an extensive set of input/output capabilities • Stream based input/output relies extensively on operator overloading as well as being object oriented • Specific I/O routines are called depending on the data type of the object being input or output • Users can specify how to perform I/O for objects or programmer defined types

  42. The ostream and istream classes • The iostream library provides the ostream class for stream-based output and the istream class for stream-based input • These classes provide overload functions for the << and >> operators • << - stream insertion operator for output • >> - stream extraction operator for input • Overload functions for the primitive types are provided • Overload functions can easily be provided for programmer defined classes

  43. The ostream class class ostream { . . public : ostream& operator << (const char*); ostream& operator<<(char); ostream& operator<<(int); ostream& operator<<(double); ostream& operator<<(const void*); . . }

  44. All C++ programs have access to an ostream object cout • ostream overload functions automatically called by matching the data types • The << operator has left-to-right associativity so multiple objects can be output in one statment

  45. Example int x=10; cout << "x = " << x; // Outputs “x= 10” • Implemented as (cout.operator<<(“x=“)).operator<<(x); • Overload functions ostream.operator<<(char*) and ostream.operator<<(int) called

  46. The istream class class istream { . . public : istream& operator >> (char*); istream& operator>>(char&); istream& operator>>(int&); istream& operator>>(double&); . . }

  47. All C++ programs have access to an istream object cin • istream overload functions take reference arguments as they modify the arguments • The >> operator has left-to-right associativity so multiple objects can be input in one statement • operator>>() functions skips white space characters (blanks, tabs, newline, formfeed, carriage return) on the input stream in the same way as scanf() )

  48. Example void main() { int i1, i2; float f; cout << "Input 2 ints and a float \n"; cin >> i1 >> i2 >> f; }

  49. Using right to left associativity, the input is implemented as(((cin >> i1) >> i2) >> f); • operator>>(int&) is called twice and operator>>(float&) is called once • The values input must be separated by whitespace characters (blanks, tabs, cr) and not commas!

  50. User defined input/ouput • We can add overloaded operator>>() and operator<<() functions to our own classes • They must be overloaded as friend functions of the class and not member functions • Obviously they are implemented as binary operators with the first operand cin or cout • We must remember to return a reference to an ostream or istream object

More Related