Download presentation
Presentation is loading. Please wait.
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 the array 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
s1.p s2.p “a”, “b”, “c”, “d”, “e”, “f”, After assignment Before assignment “d”, “e”, “f”, s2.p s1.p “a”, “b”, “c”,
28
An assignment overload operator defined as String& 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
s1.p s2.p “a”, “b”, “c”, “d”, “e”, “f”, After assignment Before assignment “d”, “e”, “f”, s2.p s1.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 =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 > 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 Implemented as (cout.operator<<(“x=“)).operator<<(x); Overload functions ostream.operator<<(char*) and ostream.operator<<(int) called int x=10; cout << "x = " << x;// Outputs “x= 10”
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
51
Example We can add input and output facilities to our Date class Enables a user friendly way of displaying and inputting the current date
52
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;} friend ostream& operator<<(ostream&, const Date&); friend istream& operator>>(istream&, Date&); Date& operator++();// pre-increment Date operator++(int);// post-increment int endOfMonth(int) const; int leapYear(int) const; };
53
ostream& operator<<(ostream& os, const Date& d) { static char* monthName[13]={“”,”Jan”,”Feb”,”March”,”April”,”May”,”June”,”July”,”Aug”,”Sept”,”Oct”,”Nov”,”Dec”}; os<<monthName[d.month] << “ “ << d.day << “ “ << d.year; << “ “ << d.year; return os; }; istream& operator>>(istream& is, Date& d) { cout<<“Input the date (dd/mm/yy) “; is >> d.day >> d.month >> d.year; return is; };
54
void main() { Date d; cout << “Input the date : “; cin >> d; cout << “The date is “ << d; }
55
And finally….. We have covered some basic ideas about operator overloading and streamed I/O Operator overloading provides user defined classes with elegant notation for expressing manipulations of objects There are more advanced applications of operator overloading such as –Overloading new and delete for advanced memory management routines –Overloading -> and * for ‘smart’ pointer access
56
There are also more advanced features of streams including : –File access. Streams can be attached to filenames allowing the whole set of stream functionality to be applied to file I/O –Formatting. Field width, field precision (for floating point output), formatting of integer output (decimal, hex, octal). –String streams. Streams can be attached to arrays of characters which allows easy conversion of strings into constituent parts (similar to sprintf() and sscanf())
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.