Presentation is loading. Please wait.

Presentation is loading. Please wait.

Advanced Issues on Classes and OOP

Similar presentations


Presentation on theme: "Advanced Issues on Classes and OOP"— Presentation transcript:

1 Advanced Issues on Classes and OOP
Part 1 - constructor, destructor, copy constructor, deep copy, assignment operator -Tapestry Section and partially (a bit limited) - Horton parts of Chapter 7 and 8 (detailed discussion and some parts are not needed) operator overloading Horton Chapter 8, starting page 345 (a bit better) Appendix E and 9.4 in Tapestry Part 2 - iterators and friend classes, using static variables in classes Part 3 sharing a variable among several objects of the same class (reference variables, pointers)

2 Constructor Constructors are special member fuctions of a class.
When an object of a class is created, C++ calls the constructor for that class. Properties: Constructors have the same name as the class. Constructors do not return any values Constructors are invoked first when a class is initialized. Any initializations for the class members, memory allocations are done in the constructor. Constructors may or may not take parameters A constructor with no parameter is called default constructor

3 Default Constructor Constructor with no parameter.
Node::Node() { //initialization code } A constructor with parameters can also be default if the default values of these parameters are provided at declaration. In this case, if no argument is provided when the constructor is called, default values are used. If argument(s) is/are provided, given arguments are used during construction. Node::Node(int num = 0, Node * ptr = NULL) Node myNode(); Node myOtherNode (10); Node * ptr = new Node(); Node * ptr2 = new Node(10, ptr); Node * ptr3 = new Node (100); Draw during lecture

4 Default Constructor What happens if the programmer does not define any constructor (with and without parameters)? The compiler attempts to generate a default constructor Not visible to the programmer When an object is created, this default constructor is invoked automatically This compiler-generated constructor allocates memory for the class private members and calls default constructors of them (if applicable)

5 Constructor Example //point.h #ifndef POINT_H #define POINT_H class Point { public: Point(); //default constructor Point(int xx, int yy); // constructor with parameters . . . private: int x; int y; }; #endif // default constructor Point::Point() { x = 0; y = 0; } // constructor with parameters Point::Point(int xx, int yy) { x = xx; y = yy; Instead of these two constructors you can have Point::Point(int xx=0, int yy=0) { x = xx; y = yy; } If no constructor was defined, then a created object would have two integers x and y with no initial values

6 Using Initializer List in Constructor
Syntax ConstructorHeader : privatedata (value), privatedata(value), . . . { // other constructor code comes here, executed after assigning values in the initializer list } Examples // default constructor Point::Point() : x(0), y(0) { } //Alternative default constructor implementation Point::Point() : x(0) y = 0; // constructor with parameters Point::Point(int xx, int yy) : x(xx), y(yy)

7 Destructor Tapestry pp. 624-625
Each class should also have a destructor which should take care of returning any remaining dynamically allocated memory back to heap. The destructor has name ~classname() (put ~ character before the class name) No parameters This is in the class definition under public class LinkedList { private: node * head; int size; public: LinkedList (); ~LinkedList (); //destructor void printList(); void addToBeginning(int n); //more functions here };

8 Destructor Destructor function is conceptually the inverse of constructor They are called when an object is destroyed. Typical job is to return dynamic memory allocated (using new, malloc, calloc) to the heap. You do not need to take an action for automatic (regular) data members (i.e. the ones allocated from the runtime stack). They are automatically deallocated. LinkedList::~LinkedList () { node * ptr = head; while (ptr != NULL) node * temp = ptr->next; delete ptr; ptr = temp; }

9 Destructor The destructor is called automatically when the object goes out of scope. For local (automatic) objects, when the block in which the object is created finishes For static or global objects, when the program finishes For dynamically allocated objects, when they are deleted using delete or free If you do not provide a destructor for your class, the compiler provides one, but that’s just an empty function, so it does not serve any purpose beside providing a required function. This may be enough when your class object does not use any heap memory. For instance a Date class that has only int, day, month, year member variables does not need anything specific in its destructor. You can also explicitly call a destructor, but this is not so needed in practice. LinkedList mylist; . . . mylist.~LinkedList(); //mylist destroyed

10 Destructor int CountUnique (ifstream & input) { string word;
LinkStringSet set; //similar to a linkedlist of strings; also has a size field // LinkStringSet is a Tapestry class while (input >> word) //by inserting words into a “set” which set.insert(word); //skips the insertion if the element is already in the set //we can count how many elements (words) are unique return set.size(); } What happens to set when this function returns? At the end of the function, destructor of LinkStringSet class is called on set since set goes out of scope.

11 Destructor Since the compiler makes this call automatically, it creates a a dummy destructor for each class that does not contain a destructor. So, if the programmer has not supplied a destructor, the one which is created by the compiler is called This basically prevents the program from not compiling, but as a dummy function, it is not guaranteed to do the right thing (e.g. doesn’t free all memory) if the programmer has supplied one, compiler won’t generate a dummy destructor Remember that the (real) destructor should free all memory taken with “new” or other dynamic memory allocation functions. Let's see linkedlistextra class and demo (linkedlistextra.cpp and linkedlistextrademo.cpp) for live examples

12 Copy Constructor, Operator Overloading, Assignment Overview and Purpose
Now that you know how to write a class, with constructor, destructor and necessary member functions, we will look at more fancy material. We are going to see how the functionality shown in red, can be achieved: linkedlist list1, list2, list3; list1.InsertOrdered(5); list2.PrintList(); //you know how to do these list2 = list1; //assign list1 to list2 list3 = list1 + list2; //addition of lists, with suitable meaning linkedlist list4(list1); //construct list4 from list1 First two are typically operators that you take for granted for built-in types. Now you are asked to give the same functionality (as long as meaningful for your class).

13 Copy Constructor Special constructor called when an object is first declared and initialized from another object of the same type Example: LinkedList list3(list); //list is previously defined Date today; Date tomorrow (today+1); Date yesterday (today-1); Date yesterday = tomorrow; //this is *not* a copy constructor Syntax Examples for the Copy constructor definition (also add a prototype to class definiton): LinkedList::LinkedList (const LinkedList & copy) { //copy constructor code comes here } Date::Date (const Date & d)

14 Copy Constructor for LinkedList Class
You have to add a prototype to the class definition as well. Caution: Always use const reference parameter here. class LinkedList { private: node * head; int size; public: LinkedList (); LinkedList (const LinkedList &); //copy constructor ~LinkedList (); //destructor void printList() const; void addToBeginning(int n); // more functions here }; LinkedList::LinkedList (const LinkedList & copy) head = copy.head; size = copy.size; }

15 Copy Constructor The copy constructor in the previous slide makes "shallow copy" ; only the private data of a class instance is copied. In the case of a linked list, this would be the head pointer, NOT the whole list The new list share the same memory as the old one! Let's give an example Suppose list1 is like the one on the right. After LinkedList list2(list1); the lists become as follows list node * head int size = 3 list node * head int size = 3 node * head int size = 3 list2

16 Copy Constructor For every class, there is a default copy constructor (compiler provided). Compiler provides this if you do not declare a copy constructor. Work exactly as shallow copy mentioned before Simply copies the value of each instance variable (e.g. head pointer, size for linked lists) from one object to the other. The entire list is not copied. Shallow copy may not be what we want. It may cause several problems. See next slide and let's see linkedlistextra class and demo (linkedlistextra.cpp) for example cases. Also try that we do not actually need to define a copy constructor for shallow copy; default copy constructor would do the same job. It is always better to use const reference parameters instead of value parameters for class types. In value parameter passing, implicitly copy constructor is invoked. Using const reference parameter avoids this and unexpected effects of default/shallow copy constructors and destructors.

17 Why Shallow Copy is Bad? LinkedList list1;
for (int k=0; k < 4; k++) { list1.addToBeginning(k+1); } list1.printList(); LinkedList list2(list1); list1.deleteList(); list2.printList(); We want to be able to do this! But the program crashes since the list is deleted from the memory after list1.deleteList(); and list2.printList(); tries to access the deleted linked list.

18 Good Copy Constructor – Deep Copy
If the object has dynamically allocated memory, such as linked lists, you may want to copy the entire memory location (the linked list) to another object You create a clone called "deep copy" since you trace all memory locations that the object is using. You have to write the necessary function as a deep copy constructor (not compiler provided) LinkedList list2(list1); list node * head int size = 3 node * head int size = 3 list

19 Deep Copy – How? No change in the class definition. Only implementation is different See this and next slide Also see linkedlistextra class and demo (linkedlistextra.cpp) class LinkedList { private: node * head; int size; public: LinkedList (); LinkedList (const LinkedList &); //copy constructor ~LinkedList (); //destructor void printList() const; void addToBeginning(int n); void deleteList (); node * createClone () const; //generates the clone of the list and return the clone's head };

20 Deep Copy – How? Let's trace this on the board for a sample case
LinkedList::LinkedList (const LinkedList & copy) { head = copy.createClone(); size = copy.size; } //generates a clone of the linked list object by generating new copies of //each node and connecting them as in the original. //Returns the head of the clone list. node * LinkedList::createClone () const if (head == NULL) //if list is empty return NULL; //clone is empty as well //first generate the first clone node and connect to head of clone node * headClone = new node (head->info, NULL); node * ptr = head->next; //second node in orig. node * ptrClone = headClone; //to track the clone list while (ptr != NULL) ptrClone->next = new node (ptr->info, NULL); ptr = ptr->next; ptrClone = ptrClone->next; return headClone; Let's trace this on the board for a sample case

21 Deep Copy – How? Another example using LinkedStringSet class of Tapestry LinkedStringSet list2(list1); //usage example LinkStringSet::LinkStringSet (const LinkStringSet& set) : myFirst(new Node("header",set.clone())), //same as: myFirst = new Node("header",set.clone())); mySize(set.size()) //see the color illustration below { // deep copy made in an initializer list – no code left for here } The newly created object is calling a helper function clone() Shown in the next slide list2.myFirst list1.myFirst NULL “header” ………. NULL

22 Deep Copy – How? LinkStringSet::Node * LinkStringSet::clone() const {
Node dummy ("header",NULL); Node * dest = &dummy; Node * temp = myFirst->next; while (temp != NULL) dest->next = new Node(temp->info,NULL); dest= dest->next; temp = temp->next; } return dummy.next; Note here that the dummy node is a normal variable on the stack and thus its scope ends when we leave this function. However, due to the use in linkstringset class, the real dummy node is created outside see prev. slide: myFirst(new Node("header",set.clone()))

23 Operator Overloading

24 Overloading in C++ Overloading is the practice of supplying more than one definition for a given function name. The compiler picks the appropriate version of the function or operator based on the arguments with which it is called. In case of an ambiguity, syntax error occurs Both free and member functions can be overloaded double max( double d1, double d2 ) { if ( d1 > d2 ) return d1; else return d2; } int max( int i1, int i2 ) if ( i1 > i2 ) return i1; return i2; int main() int i = max( 12, 8 ); //calls second one double d = max( 17.4, 32.9 ); //calls first one What happens if one argument is integer but the other is real?

25 Writing Function for Operators and Operator Overloading
In C++, you can write functions for operators as well. When such an operator is used with its operands, that function is called. We will see syntax and example in the coming slides. You can overload operators as you can overload functions. Actually overloading for operators is inevitable since the defined operators of C++ already have other meanings Compiler differentiates among different meanings of the same operator using the types of the operands

26 Assignment Operator Let’s overload (i.e. make it work for your own class) the assignment operator. Usage: list2 = list1; Compiler interprets this as: list2.operator = (list1); Syntax for function header for operators Return_Type classname::operator Operator_Symbol (parameters) Syntax Examples for function headers that define operators: const myclass & myclass ::operator = (const myclass & rhs) const linkedlist & linkedlist::operator = (const linkedlist & list1) Caution: Use const-reference parameters instead of value parameters to avoid unexpected tricks of shallow copy constructors and destructors

27 Implementation of Assignment Operator
Similar to the deep copy constructor, but the assignment operator is called to reinitialize an object that has already been constructed. Since the object already exists, more bookkeeping is necessary. We will see the implementation of assignment operator next, but before that we will see this keyword.

28 this When you apply a member function to an object
say calling member function func on object obj obj.func() the program invisibly executes this = & obj; before the function starts to execute. Thus, this is a pointer to the object on which the function is being executed (in our example obj). therefore, the function can use *this to refer to the current object on which the function is executing

29 Implementation of Assignment Operator
Prototype added to the class declaration (LinkedListExtraOper.h) class LinkedList { private: node * head; int size; public: LinkedList (); LinkedList (const LinkedList &); //copy constructor ~LinkedList (); //destructor void printList() const; void addToBeginning(int n); void deleteList (); const LinkedList & LinkedList::operator = (const LinkedList & rhs); node * createClone () const; };

30 Implementation of Assignment Operator
Operator function is defined in the class implementation file (LinkedListExtraOper.cpp in our case) When called as a = b; a (the left hand side – lhs) is the object on which the function is running (i.e. *this) b is the parameter (i.e. rhs in our example) const LinkedList & LinkedList::operator = (const LinkedList & rhs) { if (this != &rhs) deleteList(); head = rhs.createClone(); size = rhs.size; } return *this; If not self assignment - we need this guard since we clear the lhs before the assignment. If we don't have this, in case of self assignment (e.g. a = a), the content is deleted before copying. Delete the lhs – new data is coming; so old data should go Make deep copy and store in the lhs All assighments should return lhs due to cascaded assignments such as a = b = c

31 Demo Program (LinkedListExtraOper.cpp)
LinkedList list1, list3; for (int k=0; k < 4; k++) { list1.addToBeginning(k+1); } cout << "list1 contains:\n"; list1.printList(); LinkedList list2(list1); cout << "list2 is created from list1 using copy constructor\n"; list3 = list1; cout << "list1 is assigned to list3\n"; list1.deleteList(); cout << "list1 is deleted\n"; cout << "\nlist2 contains:\n"; list2.printList(); cout << "\nlist3 contains:\n"; list3.printList(); list1.addToBeginning(100); list1.addToBeginning(50); cout << "list1 is reinitialized and contains:\n"; list2 = list1; // same as list2.operator = (list1); cout << "list1 is assigned to list2\n"; cout << "list2 contains:\n"; cout << "list2 is assigned to itself\n"; cout << "list2 contains:\n"; list2 = list2; //try this also after deleting if (this != &rhs) at operator definition Let's run this Extras Try list3 =list2 = list1 as instructed in the code Try the scenario with shallow copy constr. and non-ref return type mentioned at the end of the code

32 Assignment Operator: Implementation in LinkStringSet class
const LinkStringSet& LinkStringSet::operator = (const LinkStringSet& set) { if (&set != this) //to prevent misbehaviour if a=a is used { reclaimNodes(myFirst->next); //free memory of lhs myFirst->next = set.clone(); //copy rhs mySize = set.size(); } return *this; //return lhs for situations when //a = b = c; is used } //which is equal to the statement: //a = (b = c)

33 Linksetdemo.cpp Check out LinkStringSet.h, cpp and this demo program
int main() { LinkStringSet a,b; a.insert("apple"); a.insert("cherry"); cout << "a : "; Print(a); //cherry apple 2 b = a; cout << "b : "; Print(b); //cherry apple 2 a.clear(); cout << "a : "; Print(a); // //as intended with =, provided by deepcopy in linkstringset.cpp return 0; } Check out LinkStringSet.h, cpp and this demo program

34 Tapestry Chp. 9.4 clockt.h and clockt.cpp
We will now look at the Tapestry’s ClockTime class for: more operator overloading class design How to design a clock time class? to represent a clock time object: 19:59:00

35 ClockTime Class class ClockTime { public: ClockTime();
ClockTime(int secs, int mins, int hours); int Hours() const; // returns # hours int Minutes() const; // returns # minutes int Seconds() const; // returns # seconds string tostring() const; // converts to string bool Equals(const ClockTime& ct) const; // true if == ct bool Less (const ClockTime& ct) const; // true if < ct const ClockTime & operator += (const ClockTime & ct); private: void Normalize(); //normalized such that < 60 secs, < 60 min int mySeconds; // constrained: 0-59 int myMinutes; // constrained: 0-59 int myHours; };

36 ClockTime Class ClockTime::ClockTime (int secs, int mins, int hours)
: mySeconds(secs), myMinutes(mins), myHours(hours) // postcondition: all data fields initialized { Normalize(); } void ClockTime::Normalize() myMinutes += ... mySeconds ... myHours += ... myMinutes ...

37 ClockTime Class ClockTime::ClockTime (int secs, int mins, int hours)
: mySeconds(secs), myMinutes(mins), myHours(hours) // postcondition: all data fields initialized { Normalize(); } void ClockTime::Normalize() myMinutes += mySeconds/60; // overflow from secs to myMinutes mySeconds %= 60; // now between 0 and 59 myHours += myMinutes/60; // overflow from myMinutes to myHours myMinutes %= 60; // now between 0 and 59

38 Helper Functions of the ClockTime class
These will be used in operator overloading. They implement the straightforward meaning (make sure you understand): bool ClockTime::Equals (const ClockTime& c) const //usage: c1.Equals(c2) // postcondition: returns true if this object’s time == c { return ( Hours() == c.Hours() && Minutes() == c.Minutes() && Seconds() == c.Seconds() ); }

39 Helper Functions of the ClockTime class
These will be used in operator overloading. They implement the straightforward meaning (make sure you understand): bool ClockTime::Less (const ClockTime& c) const // postcondition: returns true if this object’s time < c { return ( Hours() < c.Hours() ) || ( ( Hours() == c.Hours() ) && ( ( Minutes() < c.Minutes() ) || ( ( Minutes() == c.Minutes() ) && ( Seconds() < c.Seconds() ) ) ) ); }

40 Overloading Operators
Now let’s overload the operator >=. First notice that we should be able to use the “>=“ operator with clocktimes in 3 ways: ClockTime c1, c2; if ( c1 >= c2) if ( c1 >= 1) (let this mean “c1 is more than or equal to 1” hr) if ( 1 >= c1) ...

41 Overloading –complete case for >=
Let's detail a bit ClockTime c1, c2; if ( c1 >= c2) calls c1.operator>=(c2) bool ClockTime::operator>=(const ClockTime & rhs) { return ! ( Less(rhs) ); //uses the helper function Less() of c1. } if ( c1 >= 1) calls c1.operator>=(1) : let this mean "is the time more than 1 hr" bool ClockTime::operator>=(const int & rhs) { return ( Hours() >= rhs ); //uses the accessor function Hours() of c1. //alternative: { return ( myHours >= rhs ); } //since the member functions can access private data if ( 1 >= c1) cannot call 1.operator>=(c1) since 1 is a constant, so this version of the operator>= must be a free function

42 Overloading –complete case for >=
if ( c1 >= c2) calls c1.operator>=(c2) bool ClockTime::operator>=(const ClockTime & rhs) { return ! ( Less(rhs) ); //uses the helper function Less() of c1. } if ( c1 >= 1) calls c1.operator>=(1) : let this mean "is the time more than 1 hr" bool ClockTime::operator>=(const int & rhs) { return ( Hours() >= rhs ); //uses the accessor function Hours() of c1. //alternative: { return ( myHours >= rhs ); } //since the member functions can access private data if ( 1 >= c1) cannot call 1.operator>=(c1) since 1 is a constant, so this version of the operator>= must be a free function bool operator>=(const int & lhs, const ClockTime & rhs) { ClockTime temp(0, 0, lhs); return temp >= rhs;

43 Operator += Usage: Interpreted as: c1 += ct; c1.operator+=(ct);
const ClockTime & ClockTime::operator += (const ClockTime & ct) // postcondition: add ct, return normalized result { }

44 Operator += Usage: Interpreted as: c1 += ct; c1.operator+=(ct);
const ClockTime & ClockTime::operator += (const ClockTime & ct) // postcondition: add ct, return normalized result { mySeconds += ct.mySeconds; myMinutes += ct.myMinutes; myHours += ct.myHours; Normalize(); return *this; }

45 Operator += why operator += returns *this?
same arguments as for the assignment operator (=) in case someone uses: e.g. c1 = c2 += c3; or c1 += c2 += c3 Remember assignment operators' associativity is from right to left e.g. c1 = c2 = c3 means c1 = (c2 = c3) so c2 = c3 should return c2 so that it can be assigned to c1 Similarly c1 += c2 += c3 means c1 += (c2 += c3) This is a bit cryptic (do not use statements like this), but in general one should design the same behavior as it exists for built-in types (it is legal to say this for ints and other built-in types)

46 clockt.cpp – other overloaded operators
In clockt cpp, only some forms of the overloaded operators are provided (apparently they did not see a good use comparing clocktime objects to constants etc.) and most are declared as free functions: ostream & operator << (ostream & os, const ClockTime & ct); ClockTime operator + (const ClockTime & lhs, const ClockTime & rhs); bool operator == (const ClockTime& lhs, const ClockTime& rhs); bool operator != (const ClockTime& lhs, const ClockTime& rhs); bool operator < (const ClockTime& lhs, const ClockTime& rhs); bool operator > (const ClockTime& lhs, const ClockTime& rhs); bool operator <= (const ClockTime& lhs, const ClockTime& rhs); bool operator >= (const ClockTime& lhs, const ClockTime& rhs); However, most of these particular functions can - or even should be - member functions since they have a left-hand-side which is a clocktime object, they can be member functions (which is better) The only exception here is the << operator which has to be a free function since lhs is not a clocktime object When we are writing versions of the overloaded operators which has constants on the left-hand-side, they have to be free functions

47 Operator + c = c1 + c2; c = (operator+(c1,c2));
Usage: Interpreted as: c = c1 + c2; c = (operator+(c1,c2)); operator+ is a free function How can you simply implement it? Hint: using the previously defined operator+= of the class: ClockTime operator+ (const ClockTime & lhs, const ClockTime & rhs) // postcondition: return lhs + rhs (normalized for myMinutes, mySeconds) { }

48 Operator + The free function operator+ can be implemented using the previously defined operator+= class: ClockTime operator+ (const ClockTime & lhs, const ClockTime & rhs) // postcondition: return lhs + rhs (normalized for myMinutes, mySeconds) { ClockTime result(lhs); //uses the default (compiler generated) copy constructor result += rhs; //uses the previously defined operator+= return result; } Why is the return type just ClockTime but not a reference as in = and +=? We return result, which is a local object. Returning reference of a local object/variable is very problematic since the scope ends after the function. In the current form of ClockTime class this may not cause a problem but if we had a destructor for ClockTime, that would be a problem. Let's simulate this case by having a destructor that initializes private data members. When we return value, copy constructor is implicitly called, a copy of the returned object is created and this is returned to the caller function.After using it at the caller function, the copy constructed value is destructed. This is exactly what we want. Why did we need result? Why not simply have only return lhs+= rhs? Because you cannot change the value of a const reference parameter, compiler does not allow this. What about using value parameters for lhs and rhs? This time you could just have return lhs+= rhs; in the function body; but causes unnecessary copy

49 Friend functions A friend function is used for accessing the non-public members of a class. A friend function is an ordinary (free) function or a member function of another class. The friend function is written as any other normal function. However, you have to add a prototype for the function in the class declaration by putting the keyword friend at the beginning Use friend keyword in the prototype only, not in the function definition. It is possible to declare a function as friend in any number of classes. When a class is declared as a friend, the friend class has access to the private data of the class that made this a friend. Will see friend classes later The function is invoked without the use of an object: objects are passed to it as arguments.

50 An Example Friend Function
Let's overload + operator (two operands but the first one is an integer, second one is a clock) ClockTime operator + (int lhs, const ClockTime& rhs) Adds lhs hours to rhs and returns the new clock value. Let's implement this as a free function. Actually ClockTime class has enough accessors for implementation, but let's do it by accessing private data members. We add the following function to clockt.cpp { ClockTime temp (rhs.mySeconds, rhs.myMinutes, rhs.myHours + lhs); return temp; } Since we use private data members of the class, this function should be a friend function. To do so also add the following prototype to clocktime.h within the class declaration friend ClockTime operator + (int lhs, const ClockTime& rhs);

51 Overloading I/O operators
ostream & operator << (ostream & os, const ClockTime & ct) // postcondition: inserts ct onto os, returns os // format is h:m:s { os << ct.tostring(); return os; } string ClockTime::tostring() const ostringstream os; //to use a string as a stream os.fill('0'); //unused spaces on the left fill with 0s os << Hours() << ":" << setw(2) << Minutes() << ":" << setw(2) << Seconds(); 1:09:59 return os.str();

52 Overloading I/O operators
ostream & operator << (ostream & os, const ClockTime & ct) // postcondition: inserts ct onto os, returns os // format is h:m:s { os << ct.tostring(); return os; } operator << cannot be a member function of the class (in general) since lhs is a stream cout << c1; ostream (output streams) has to be passed as reference Since the ostream is modified when you put something on the stream operator << must return os in case we use: cout << c1 << c2; since << is left-to-right associative, the result of cout << c1 must be cout (parameter os).

53 Overloading I/O operators
istream & operator >> (istream & is, ClockTime & ct) //postcondition: reads private members of ct from is, and normalize ct // returns is { is >> ct.myHours >> ct.myMinutes >> ct.mySeconds; ct.Normalize(); return is; } operator >> is to read an object from an inpuıt stream (input file stream, input string stream or just cin) operator >> cannot be a member function of the class since lhs is a stream cin >> c1; This operator function has been declared as friend of ClockTime class since we need to update private data members of ClockTime and do not have mutators for this. However, normally it'd be better to have mutators and change the private part via mutators. istream (input streams) has to be passed as reference Since the istream is modified when you read something from the stream operator >> must return is in case we use: cin >> c1 >> c2; since >> is left-to-right associative, the result of cin >> c1 must be cin (parameter is).

54 Other overloaded operators
bool operator == (const ClockTime& lhs, const ClockTime& rhs) { return lhs.Equals(rhs); } bool operator != (const ClockTime& lhs, const ClockTime& rhs) return ! (lhs == rhs); ... Let's see all of these in clockt.h and clockt.cpp

55 What happened to operator = ?
Have you realized that ClockTime class do not have operator = ? Neither member nor free function But we can use assignment operator (see useclock.cpp) If there is no = operator implemented in overloaded operators compiler provides a default implementation automatically Default assignment operator makes shallow copy as in default copy constructor Only private data members are copied If you are happy with that, then you do not need to write a function for = This is acceptable for ClockTime class since it does not use any dynamic memory from heap

56 Using & in return values (reference return type) – Horton pp. 219- 222
Sometimes we use reference return values such as const ClassName & FunctionName (Parameters) ClassName & FunctionName (Parameters) For example, operator += or ClockTime class and operator = LinkedList class, Operator << of ClockTime class What is the difference between these and using just the ClassName as the return type (value return)? ClassName FunctionName (Parameters) Using just the class name (value return) generates a copy of the return value when returning This invokes copy constructor (user defined or, if it does not exist, default one) This is risky if not implemented (shallow copy problem) And it is ineffient to call the copy constructor for this purpose

57 Using & in return values (reference return type)
Sometimes the object to be returned has already been defined outside of the function (or dynamically allocated within the function) In such a case, we can directly return that object without a copy operation This is called reference return The mechanisms with & does this. Be careful, do not return a local variable using this method Local variable gets lost after the function Probably your program crashes What is the effect of using const here? Not much, a very defensive programming technique If const is used, the compiler does not allow you to change function's returning value Let's see this on an example (see refreturn.cpp)


Download ppt "Advanced Issues on Classes and OOP"

Similar presentations


Ads by Google