Classes with Pointers --------------------- Reading: Chap 10.3, 18.3-18.4 0) Introduction - previously, we have discussed the notion of classes - those classes had simple data types in them, int, float, etc. - they did not have pointers in them - classes that have pointers in them introduce several issues - we will look at these here - outline - example pointer class - assignment operator - copy constructor - destructor - reference variables A) Time Class With a Pointer 1) Original class class Time { private: int hour, minute, second; public: // Constructors Time(); Time(int h, int m, int s); // Overloaded operators Time operator+(Time &); Time operator-(Time &); // Other functions void AddtoTime(int h, int m, int s); void Print(); }; 2) Pointer class struct _time { int hour; int minute; int second; }; class Time { private: struct _time *tt; // replace the 3 ints with pointer to struct public: same as above }; - changes in implementation - replace hour with tt->hour, etc. - e.g. constructor shown below - similar changes to other functions Time::Time(int h, int m, int s) { tt = new struct _time; // need to allocate _time tt->hour = h; tt->minute = m; tt->second = s; } - why would you do this? - save space in some cases - e.g., (bad) if tt is not needed for all Time classes - sharing of data (many to one relationship) - avoids duplication and inconsistency B) Assignment Operator - now consider the following Time x(1, 0, 0); Time y; x y ______ ______ ______ ______ | | | | | | | | | tt |---->| 1 | | tt |---->| 0 | |______| |______| |______| |______| | | | | | 0 | | 0 | |______| |______| | | | | | 0 | | 0 | |______| |______| - what does the following mean? y = x; - each C++ class comes with a default assignment operator - makes a field-by-field copy of the RHS to the LHS - old implementation: copies x.hour to y.hour, x.minute to y.minute, etc. - new implementation: pointer tt is copied - show picture x.Print() y.Print() y.AddtoTime(1, 0, 0) y.Print() x.Print() - problem with default assignment operator - it copies the pointer and not the data - C++ compiler doesn't know better and makes SHALLOW copy - memory leak - solution: assignment operator must be redefined - whenever classes have dynamically allocated structures (with pointers) Time Time::operator=(Time t) // not ideal syntax, will fix later { tt->hour = t.tt->hour; tt->minute = t.tt->minute; tt->second = t.tt->minute; return *this; // why is this returned? } - show picture - does this really solve the problem? - how are parameters passed and the return value returned? - pass and return by value - parameter t is copied - how is the copy done? - SHALLOW copy C) Copy Constructor - we have fixed the assignment operator, but what about object copies - each C++ class comes with a default copy constructor - invoked when an object is created and initialized from another object - the second object must be of same type - makes a field-by-field copy of the RHS to the LHS - function prototype: Time(const Time &) // explain const and & later - in above function, two shallow copies made - when parameter is passed - when return is executed - in above case, the function actually works fine - when parameter is passed, an additional copy of y is made - this copy is destroyed on function return - when return is executed, an additional temp. copy of x is made - this copy is then destroyed - the copy is never used since the return value of (y = x) is not used - no observable harmful effects - examples when copy constructor is called Time y(x); // x is an already existing object // invokes y.Time(x), y is created and init. from x Time y = x; // doesn't invoke assignment but copy constructor Time *py = new Time(x); // calls copy constructor to copy x - default copy constructor is shallow - has the same problems as default assignment - redefinition of copy constructor Time::Time(const Time & t) { tt = new struct _time; // creates the _time object *tt = *t.tt; // default assignment of _time struct! } - explain how it works - remember it is a constructor, no return value - parameter must be passed by reference! - pass by value simply won't work in this case (compiler error!) - const ensures that we don't modify t by mistake - e.g. const int x = 5; x = 6; // error - for assignment operator, we can avoid the two copies - we can pass the parameter by reference - return by reference Time & // added & Time::operator=(const Time & t) // added const and & { tt->hour = t.tt->hour; tt->minute = t.tt->minute; tt->second = t.tt->minute; return *this; } - compiler provides a default constructor - does nothing (unlike default assignment and copy constructor) - if any other constructor defined, compiler doesn't provide default D) Destructor - method that is called when an object is destroyed - when is an object destroyed - global variables: when program quits - local variables: when variable goes out of scope - includes temporary variables Time Time::operator+(Time & t) <- no copy { int TotalTime; Time sum; <- sum constructor ... return (sum); } <- sum destructor <- temp copy constructor and destructor - heap variables: when delete is called Time *t = new Time; ... delete t; - destructor is rarely (if never) called explicitly! - in old implementation - destructor didn't need to do anything - in new implementation - destructor needs to delete the dynamically allocated struct - in general, if you use "new" in the constructor - or perhaps in some other member function to allocate storage dynamically - then you need to use "delete" in the destructor - show with a picture Time::~Time() { delete tt; } - with this destructor, the copy-based assignment operator would not work - with the default copy constructor - explain why E) Reference Variables F) Tutorial - how many copies are made when passing parameters and return values by copy - when and when not to return by reference - Talk about const - "this" pointer is const in accessor functions - e.g. int getHour() const;