Cpt S 223 – Advanced Data Structures C++ Review 2 Nirmalya Roy School of Electrical Engineering and Computer Science Washington State University Topics Parameter Passing Return Passing Destructor, copy constructor, operator= Templates Destructor Copy constructor operator= Problems with default destructor, copy constructor, operator= Function templates Class templates Summary Parameter Passing C++ parameter passing techniques Call by value Call by constant reference Appropriate for small objects that should not be altered by function Appropriate for large objects that should not be altered by function Call by reference Appropriate for all objects that may be altered by function Call by constant reference Call by value Call by reference double avg(const vector<int>& arr, int n, bool & errorFlag); Average of the first n integers in arr, and set errorflag to true if n is larger than arr.size() or smaller than 1 Parameter Passing Rules If the formal parameter should be able to change the value of actual argument, then use call by reference Else if the value of the actual argument cannot be changed by the formal parameters: If the type is a primitive type, use call by value If the type is a class type, then would generally be passed using call by constant reference However if the class types that are small (with only a single built-in type) can be passed using call by value instead of call by constant reference Return Passing Objects can also be returned using return by value, return by constant reference, and occasionally return by reference For the most part, do not use return by reference It is safe to return by value If an object of a class type is being returned, it may be better to use return by constant reference avoid the overhead of a copy Make sure the object returned will persist after returning from the function call Destructor, Copy Constructor, Operator= In C++, classes come with 3 special functions automatically written for you Destructor Copy constructor operator= These default functions may or may not be appropriate for your class Destructor The destructor is called when an object goes out of scope or is subject to a delete By default, it calls the destructor on all data members Destructor should free up any resources that were allocated during the use of the object (i.e. delete objects creating using new), closing any opened files, etc Copy Constructor A special constructor used to construct a new object, initialized to a copy of the same type of object Copy Constructor (cont’d) class IntCell { public: IntCell() { storedValue = 0; } IntCell(int initialValue) { storedValue = initialValue; } int read() { return storedValue; } void write(int x) { storedValue = x; } private: int storedValue; }; Copy constructor is called in the following cases: 1. Declaration with initialization IntCell B = C; IntCell B(C); 2. An object passed using call by value (instead of call by & or call by const &) 3. An object returned by value (instead of by & or const &) By default, copy constructor calls copy constructors to each data members Simple assignment for all data members with primitive types (e.g. int, char, double etc) operator= operator= is called the copy assignment operator operator= is called when objects on both sides of = are already constructed For example IntCell B(0); IntCell C(1); B = C; By default, operator= is called on each data member of objects Problems with Default Destructor, Copy Constructor, operator= class IntCell { public: explicit IntCell(int initialValue = 0) { storedValue = new int(initialValue); } int main() { IntCell a(2); IntCell b = a; IntCell c; int read() const { return *storedValue; } c = b; a.write(4); cout << a.read() << endl << b.read(); cout << endl << c.read() << endl; void write(int x) { *storedValue = x; } return 0; } private: int *storedValue; }; What is the output? What is the problem? Problems with Default Destructor, Copy Constructor, operator= (cont’d) The class uses default destructor, copy constructor, and operator= The default destructor does not delete allocated memory (memory leak), default copy constructor and operator= only perform shallow copy (copy not the object being pointed at, but simply the value of the pointer variable) What we want is deep copy (a clone of the entire object is made) For a class containing pointers, we need deep copy so we must explicitly implement the destructor, copy constructor, and operator= Problems with Default Destructor, Copy Constructor, operator= (cont’d) class IntCell { public: explicit IntCell(int initialValue = 0); IntCell(const IntCell & rhs); ~IntCell(); //copy constructor //destructor const IntCell & operator=(const IntCell & rhs); //copy assignment operator int read() const; void write(int x); private: int *storedValue; }; Problems with Default Destructor, Copy Constructor, operator= (cont’d) // Initializer List IntCell::IntCell(int initialValue) { storedValue = new int(initialValue); } // Copy Constructor IntCell::IntCell(const IntCell & rhs) { storedValue = new int(*rhs.storedValue); } //Destructor IntCell::~IntCell() { delete storedValue; Problems with Default Destructor, Copy Constructor, operator= (cont’d) //Copy Assignment Operator const IntCell & IntCell::operator=(const IntCell & rhs) { if (this != &rhs) *storedValue = *rhs.storedValue; return *this; } // Member Function int IntCell::read() const { return *storedValue; } //Member Function void IntCell::write(int x) { *storedValue = x; } Templates Templates are for designing type-independent data structures and algorithms Type-independent means that the logic of the data structures and algorithms does not depend on the type of objects (i.e. the same logic works for any data types) Templates prevent recoding a piece of code for each different type 2 types Function templates Class templates Function Templates Untemplatized Version void output(const vector<int> & v) { for (int i = 0; i < v.size(); i++) cout << v[i] << endl; } void output(const vector<float> & v) { for (int i = 0; i < v.size(); i++) cout << v[i] << endl; } void output(const vector<char> & v) { for (int i = 0; i < v.size(); i++) cout << v[i] << endl; } Templatized Version template <typename T> void output(const vector<T> & v) { for (int i = 0; i < v.size(); i++) cout << v[i] << endl; } int main() { vector<int> v1(37); vector<float> v2(40); vector<IntCell> v3(10); //additional codes to fill in the vector are not shown output(v1); output(v2); output(v3); // illegal; operator < undefined return 0; } Class Templates Zero may not be a valid Object template <typename Object> class MemoryCell { public: explicit MemoryCell(const Object & initialValue = Object()) : storedValue(initialValue) { } const Object & read() const { return storedValue; } int main() { void write(const Object & x) { storedValue = x; } MemoryCell<int> m1; MemoryCell<string> m2(“hello”); private: Object storedValue; m1.write(37); m2.write(m2.read() + “ world”); cout<<m1.read()<<endl<<m2.read()<< endl; }; return 0; } Class Templates (cont’d) MemoryCell is like IntCell class but works for any type called Object Object should have a zero-parameter constructor, a copy constructor, and copy-assignment operator Note that the default parameter for constructor is not 0, since 0 might not be a valid Object Default parameter is the result of constructing an Object with its zero-parameter constructor Note: MemoryCell is NOT a class; it is a class template MemoryCell<int> and MemoryCell<string> are the actual classes Class Templates (cont’d) In many cases, in implementing a class template, the entire class with its implementation must be placed in a .h file Summary We have reviewed Parameter Passing Return Passing Destructor, copy constructor, operator= Templates tools for easing the design of type-independent data structures and algorithms Questions ?