chap5 - Chapter 5 An Array Class 1 Before We Begin... The...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: Chapter 5 An Array Class 1 Before We Begin... The Array class template uses some concepts you may not have seen before inline return by reference bitwise operators & and | 2 inline Functions Function calls generally take quite a few machine language instructions and slow down program execution An inline function tells the compiler to substitute the function body into the place where the function is called; therefore, in machine language, no function is actually called An inline function, therefore, is expected to execute faster 3 inline Functions (cont.) Should we inline every function? No if we have 20 calls to a function throughout the code, the 20 calls would be replaced by 20 copies of the function body (code gets too large) We should consider inlining when a function is heavily used inside of loops 4 Returning by Reference The return type can be a reference type A reference return type is used when a location, rather than a value, needs to be returned. A location can be used on the left side of an assignment, but a value can't. If function foo returns a reference to a private integer, the following function call is allowed: myObject.foo( x ) = 5; 5 Bitwise AND c = a & b; 101110 111000 101000 6 Bitwise OR c = a | b; 101110 111000 111110 7 An Array Class Template 1 2 3 4 5 6 7 6 7 8 9 // Array.h -- class template for an adjustable array // When debugging, use #define DEBUG_ARRAY above // your #include Array line. When done debugging, // comment out #define DEBUG_ARRAY for better // performance. // The constructor and the changeSize function can cause //an exception to be thrown if out of heap memory. #include <string> using namespace std; 8 An Array Class Template (cont.) 10 11 12 13 14 15 16 17 18 19 19 20 template <class DataType> class Array { public: Array( int size ); inline DataType & operator [ ]( int index ); void changeSize( int newSize ); // will not alter values // unless newSize is smaller than current capacity; // in this case, the values from 0 to newSize - 1 // will not be altered inline int length( ) const; // returns current capacity string err( ) const; // returns error message 9 An Array Class Template (cont.) 21 private: 22 DataType *elements; 23 int capacity; 24 DataType dud; 25 26 int errorCode; 27 28 }; 29 30 #include "Array.cpp" // points to the dynamic array // returned from operator [ ] if // index error occurs // contains code for error if array // misuse occurs 10 An Array Class Template (cont.) 1 2 3 4 5 6 7 8 // Array.cpp -- function definitions for an array // Error codes -- use powers of 2 // 0 No error. // 1 Nonpositive size passed into constructor. // 2 Invalid index was used. // 4 Nonpositive new size passed into changeSize // function 2 = 0010 4 = 0100 1 = 0001 11 An Array Class Template (cont.) 1 2 3 4 5 6 7 8 // Array.cpp -- function definitions for an array // Error codes -- use powers of 2 // 0 No error. // 1 Nonpositive size passed into constructor. // 2 Invalid index was used. // 4 Nonpositive new size passed into changeSize // function Example: Error code 2 can be recorded with: errorCode |= 2; 12 An Array Class Template (cont.) 1 2 3 4 5 6 7 8 // Array.cpp -- function definitions for an array // Error codes -- use powers of 2 // 0 No error. // 1 Nonpositive size passed into constructor. // 2 Invalid index was used. // 4 Nonpositive new size passed into changeSize // function Using this technique a single integer errorCode can be used to record all types of errors that have occurred. 13 An Array Class Template (cont.) 1 2 3 4 5 6 7 8 // Array.cpp -- function definitions for an array // Error codes -- use powers of 2 // 0 No error. // 1 Nonpositive size passed into constructor. // 2 Invalid index was used. // 4 Nonpositive new size passed into changeSize // function An errorCode of 0101 means errors 1 and 4 occurred 14 An Array Class Template (cont.) 1 2 3 4 5 6 7 8 // Array.cpp -- function definitions for an array // Error codes -- use powers of 2 // 0 No error. // 1 Nonpositive size passed into constructor. // 2 Invalid index was used. // 4 Nonpositive new size passed into changeSize // function If error code 2 occurred, it can be detected with: if (errorCode & 2) // false if errorCode is 0101 15 An Array Class Template (cont.) 1 2 3 4 5 6 7 8 // Array.cpp -- function definitions for an array // Error codes -- use powers of 2 // 0 No error. // 1 Nonpositive size passed into constructor. // 2 Invalid index was used. // 4 Nonpositive new size passed into changeSize // function If error code 2 occurred, it can be detected with: if (errorCode & 2) // true if errorCode is 0110 16 An Array Class Template (cont.) 9 10 11 12 13 14 15 16 17 18 19 20 21 22 template <class DataType> Array<DataType>::Array( int size ) { if ( size < 1 ) { capacity = 1; errorCode = 1; // nonpositive size } else { capacity = size; errorCode = 0; // no error } elements = new DataType [capacity]; } 17 An Array Class Template (cont.) 23 24 25 26 27 28 29 30 31 32 33 template <class DataType> inline DataType & Array<DataType>::operator [ ]( int index ) { #ifdef DEBUG_ARRAY if ( index < 0 || index >= capacity ) { errorCode |= 2; // invalid index return dud; Conditional } Compilation #endif return elements[ index ]; } 18 An Array Class Template (cont.) 34 35 36 37 38 39 40 41 42 43 44 // will not alter values unless newSize is smaller than // current capacity; in this case, the values from 0 to // newSize - 1 will not be altered template <class DataType> void Array<DataType>::changeSize( int newSize ) { if ( newSize < 1 ) { errorCode |= 4; // nonpositive new size return; changeSize function continued... } 19 An Array Class Template (cont.) 45 46 47 48 49 50 51 52 53 54 55 } DataType *newArray = new DataType [newSize]; int limit = (newSize > capacity)? capacity : newSize; for ( int i = 0; i < limit; i++ ) newArray[ i ] = elements[ i ]; delete [ ] elements; elements = newArray; capacity = newSize; ternary operator 20 An Array Class Template (cont.) 45 46 47 48 49 50 51 52 53 54 55 } DataType *newArray = new DataType [newSize]; int limit = (newSize > capacity)? capacity : newSize; for ( int i = 0; i < limit; i++ ) newArray[ i ] = elements[ i ]; delete [ ] elements; elements = newArray; capacity = newSize; if (newSize > capacity ) limit = capacity; else limit = newSize; 21 An Array Class Template (cont.) 56 57 58 59 60 template <class DataType> inline int Array<DataType>::length( ) const { return capacity; } 22 An Array Class Template (cont.) 61 template <class DataType> 62 string Array<DataType>::err( ) const 63 { 64 65 if ( errorCode == 0 ) 66 return "No error.\n"; err function continued... 23 An Array Class Template (cont.) 67 68 69 70 71 72 73 74 75 string errorMessage = ""; if ( errorCode & 1 ) { // nonpositive size errorMessage += "Nonpositive size passed into constructor, so\n"; errorMessage += "the capacity was set to 1 by default.\n"; } if ( errorCode & 2 ) // invalid index errorMessage += "Index out of range.\n"; err function continued... 24 An Array Class Template (cont.) 76 if ( errorCode & 4 ) { // nonpositive new size in 77 // changeSize 78 errorMessage += 79 "Nonpositive size passed into changeSize, so\n"; 80 errorMessage += 81 "the size of the array was not changed.\n"; 82 } 83 84 return errorMessage; 85 } 25 Using the Array Class Template 1 2 3 4 5 6 7 8 9 10 11 12 // useArray.cpp -- a program that demonstrates the use of // the Array class #include <iostream> #define DEBUG_ARRAY #include "Array.h" using namespace std; void getElements( Array<int> & numbers ); float calcAverage( Array<int> avnums ); 26 To uncover problems Using the Array Class Template (cont.) 13 int main( ) 14 { 15 Array<int> nums( 2 ); 16 17 getElements( nums ); 18 float average = calcAverage( nums ); 19 20 cout << "The average is: " << average << endl; 21 22 return 0; 23 } 27 Using the Array Class Template (cont.) 24 void getElements( Array<int> & numbers ) 25 { 26 int i = 0; 27 28 cout << "Enter a positive integer: "; 29 cin >> numbers[ i ]; 30 while ( numbers[ i ] != -1 ) { 31 i++; 32 if ( i == numbers.length( ) ) 33 numbers.changeSize( i * 2 ); 34 cout << "Enter a positive integer (enter -1 to stop): "; 35 cin >> numbers[ i ]; getElements cont... 36 } 28 Using the Array Class Template (cont.) 37 numbers.changeSize( i ); 38 39 cout << "getElements: " << numbers.err( ); 40 } sets the capacity to the exact number of elements entered. 29 Using the Array Class Template (cont.) 37 numbers.changeSize( i ); 38 39 cout << "getElements: " << numbers.err( ); 40 } Used for debugging purposes to uncover errors in the getElements function 30 Using the Array Class Template (cont.) 41 float calcAverage( Array<int> avnums ) 42 { error 43 int sum = 0; 44 for ( int i = 0; i <= avnums.length( ); i++ ) 45 sum += avnums[ i ]; 46 47 cout << "calcAverage: " << avnums.err( ); 48 return sum / float( avnums.length( ) ); 49 } 31 Running the Main Program The following output is given using the DEBUG_ARRAY Array feature in the main program: getElements: No error. calcAverage: Index out of range. The average is: [some wrong result] 32 After Debugging... After debugging, comment out all the debugging lines of code don't delete; you may need these lines of code again in future maintenance 33 After Debugging... (cont.) 1 2 3 4 5 6 7 8 9 10 11 12 // useArray.cpp -- a program that demonstrates the use of // the Array class #include <iostream> #define DEBUG_ARRAY #include "Array.h" using namespace std; void getElements( Array<int> & numbers ); float calcAverage( Array<int> avnums ); 34 After Debugging... (cont.) 1 2 3 4 5 6 7 8 9 10 11 12 // useArray.cpp -- a program that demonstrates the use of // the Array class #include <iostream> // #define DEBUG_ARRAY #include "Array.h" using namespace std; void getElements( Array<int> & numbers ); float calcAverage( Array<int> avnums ); 35 After Debugging... (cont.) 1 2 3 4 5 6 7 8 9 10 11 12 // useArray.cpp -- a program that demonstrates the use of // the Array class #include <iostream> // #define DEBUG_ARRAY #include "Array.h" using namespace std; for better array performance void getElements( Array<int> & numbers ); float calcAverage( Array<int> avnums ); 36 After Debugging... (cont.) 37 numbers.changeSize( i ); 38 39 cout << "getElements: " << numbers.err( ); 40 } end of getElements 37 After Debugging... (cont.) 37 numbers.changeSize( i ); 38 39 // cout << "getElements: " << numbers.err( ); 40 } end of getElements 38 After Debugging... (cont.) 41 float calcAverage( Array<int> avnums ) 42 { 43 int sum = 0; 44 for ( int i = 0; i < avnums.length( ); i++ ) 45 sum += avnums[ i ]; 46 47 cout << "calcAverage: " << avnums.err( ); 48 return sum / float( avnums.length( ) ); 49 } 39 After Debugging... (cont.) 41 float calcAverage( Array<int> avnums ) 42 { 43 int sum = 0; 44 for ( int i = 0; i < avnums.length( ); i++ ) 45 sum += avnums[ i ]; 46 47 // cout << "calcAverage: " << avnums.err( ); 48 return sum / float( avnums.length( ) ); 49 } 40 Destructors A destructor is a function that is called automatically whenever an object is destroyed If an Array object is declared within a function, the Array object will be destroyed at the end of the function. However, the dynamic array is not freed, causing memory leak, unless we write a destructor for the Array class. 41 Function Prototype for Destructor The function name for the destructor must be the class name preceded by a tilde. The destructor function cannot pass in parameters and it has no return type (the client does not call it) The destructor function prototype for the Array class would look like this: ~Array( ); 42 Destructor in Class Implementation File template <class DataType> Array<DataType>::~Array( ) { delete [ ] elements; } 43 Ways in which Destructors are Called When an object is declared locally within a function and the end of the function is reached (More generally) when an object is declared within a block of code (under a loop, etc.), and execution leaves the block of code At the end of program execution, the destructors for any remaining objects are called (if written) 44 Ways in which Destructors are Called (cont.) When an object is created in the heap, then delete is used on the pointer to it. For example, Array<float> *ptr = new Array<float>(10); ....// other code ..... delete ptr; Calls the destructor. 45 Problems With Passing Parameters foo( arr ); . . // other code . . . void foo( Array<int> arr2 ) { . . // function code . } arr, an Array object, is passed by value into function foo 46 Problems With Passing Parameters (cont.) foo( arr ); . . // other code . . . void foo( Array<int> arr2 ) { . . // function code . } arr is called an actual parameter the parameter in the function call arr2 is called a formal parameter the parameter in the function heading 47 Problems With Passing Parameters (cont.) foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) The address stored in { elements is the . address of a dynamic . // function code array with 4 elements . } 48 Problems With Passing Parameters (cont.) foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) 51030 25 75 10 12 { . . // function code . } 49 Problems With Passing Parameters (cont.) foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) 51030 25 75 10 12 { . . // function code . When arr is passed into function foo, a copy } of each data member's value is made. 50 Problems With Passing Parameters (cont.) foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) 51030 25 75 10 12 { arr2 int errorCode: 0 But then the int capacity: 4 elements pointer int *elements: 51030 in arr2 points to } the same array! 51 Problems With Passing Parameters (cont.) foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) 51030 25 75 10 12 { arr2 int errorCode: 0 Problem 1: int capacity: 4 Changes in the int *elements: 51030 array are reflected } back to the caller. 52 Problems With Passing Parameters (cont.) foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) 51030 25 75 10 12 { arr2 int errorCode: 0 (this isn't what we int capacity: 4 want in pass by int *elements: 51030 value!) } 53 Problems With Passing Parameters (cont.) foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) 51030 25 75 10 12 { arr2 int errorCode: 0 When foo finishes int capacity: 4 execution, arr2 int *elements: 51030 will be } destroyed... 54 Problems With Passing Parameters (cont.) foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) 51030 25 75 10 12 { arr2 int errorCode: 0 calling the int capacity: 4 destructor for arr2 int *elements: 51030 and freeing the } dynamic array! 55 Problems With Passing Parameters (cont.) foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) { } 56 Problems With Passing Parameters (cont.) foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) { One can safely say that this isn't what we want either! (Problem 2) } 57 Problems With Passing Parameters (cont.) foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) { This is called a dangling pointer or a dangling reference. } 58 Copy Constructor the Solution A copy constructor is a special class function that is called automatically when an object of the class is passed by value. The programmer does not call the copy constructor explicitly. When a copy constructor is written, data members are not copied one by one from the actual parameter to the formal parameter instead, the code in the copy constructor is followed to do the copying. 59 Copy Constructor the Solution (cont.) A copy constructor is a lot like a constructor (its name is the class name) What distinguishes it is that it passes in an object as a parameter, which belongs to the same class as the object that owns the copy constructor. The function prototype looks like: Array( const Array<DataType> & ap ); The copy constructor is called for the formal parameter (arr2 in our example) 60 Copy Constructor the Solution (cont.) Array( const Array<DataType> & ap ); The object passed into the copy constructor is the actual parameter in the function call (arr in our example) 61 Copy Constructor the Solution (cont.) Array( const Array<DataType> & ap ); The object must be passed into the copy constructor by reference passing it by value into the copy constructor would cause another call to the copy constructor (remember that the copy constructor is called automatically when an object is passed by value). Something similar to an infinite loop results. 62 Copy Constructor the Solution (cont.) Array( const Array<DataType> & ap ); It is passed by const reference since a change to the actual parameter should not occur (the compiler would catch it) 63 Shallow Copy vs. Deep Copy A shallow copy occurs when an address of a pointer in one object is copied into the pointer of another object (it led to the problems we saw earlier) A deep copy is a copy made without copying addresses; it is necessary to allocate more dynamic memory for the object copy to avoid copying addresses 64 Shallow Copy foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) 51030 25 75 10 12 { arr2 int errorCode: 0 int capacity: 4 int *elements: 51030 } 65 Deep Copy foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) 51030 25 75 10 12 { arr2 int errorCode: 0 int capacity: 4 41066 int *elements: 41066 25 75 10 12 } 66 Similar Problems from Object Assignment Recall that the object of one struct can be assigned to another object of the same struct, without overloading the = operator: myCar = yourCar; This is also true with objects that belong to the same class When using assignment on objects of the same class, all the values of the data members are copied; nothing happens with the function members 67 Similar Problems with Object Assignment (cont.) However, this means that a shallow copy takes place when data members are pointers Again, the elements pointers of both Array objects will point to the same array, creating problems 68 The Solution for Object Assignment The solution is to write an overloaded assignment operator (=) for the Array class This overloaded operator function would make a deep copy as well. 69 The deepCopy Function Both the copy constructor and the overloaded assignment operator function need to make deep copies We'll make a deepCopy function, and call the function from both of these functions 70 The Copy Constructor Definition for Array template <class DataType> Array<DataType>::Array( const Array<DataType> & ap ) { deepCopy( ap ); } 71 The Copy Constructor Process foo( arr ); First, the foo . function call is . // other code made... . . . void foo( Array<int> arr2 ) { . . // function code . } 72 The Copy Constructor Process (cont.) foo( arr ); . . // other code . . . void foo( Array<int> arr2 ) { . . // function code . } Pass by value is used, so the copy constructor is automatically called (a copy constructor is not called in pass by reference) 73 The Copy Constructor Process (cont.) foo( arr ); . . // other code . . . void foo( Array<int> arr2 ) The copy constructor for arr2 executes template <class DataType> Array<DataType>::Array( const Array<DataType> & ap ) { deepCopy( ap ); } 74 The Copy Constructor Process (cont.) foo( arr ); . . // other code . arr is passed as the parameter into the . copy constructor function heading . void foo( Array<int> arr2 ) template <class DataType> Array<DataType>::Array( const Array<DataType> & ap ) { deepCopy( ap ); } 75 The Copy Constructor Process (cont.) foo( arr ); . . // other code . the deepCopy function executes . using ap (arr) as the original . void foo( Array<int> arr2 ) template <class DataType> Array<DataType>::Array( const Array<DataType> & ap ) { deepCopy( ap ); } 76 The Copy Constructor Process (cont.) ap (or arr) The deepCopy function produces a deep copy of the ap (or arr) Array object, for arr2. int errorCode: 0 int capacity: 4 int *elements: 51030 51030 25 75 10 12 77 The Copy Constructor Process (cont.) ap (or arr) The deepCopy function produces a deep copy of the ap (or arr) Array object, for arr2. arr2 int errorCode: 0 int capacity: 4 int *elements: 51030 51030 25 75 10 12 int errorCode: 0 int capacity: 4 int *elements: 41066 41066 25 75 10 12 78 The Copy Constructor Process (cont.) foo( arr ); . . // other code . the deepCopy function returns, then . the copy constructor returns. . void foo( Array<int> arr2 ) template <class DataType> Array<DataType>::Array( const Array<DataType> & ap ) { deepCopy( ap ); } 79 The Copy Constructor Process (cont.) foo( arr ); . . // other code . arr2 now has a deep copy of the arr object . (parameter passing has completed) . void foo( Array<int> arr2 ) 80 The Copy Constructor Process (cont.) foo( arr ); arr . int errorCode: 0 . // other code int capacity: 4 . int *elements: 51030 . . void foo( Array<int> arr2 ) 51030 25 75 10 12 { arr2 int errorCode: 0 int capacity: 4 41066 int *elements: 41066 25 75 10 12 } 81 The Copy Constructor Process (cont.) foo( arr ); . . // other code . . . void foo( Array<int> arr2 ) { . . // function code . } The foo function executes. 82 The deepCopy Function is Private private: DataType *elements; int capacity; DataType dud; int errorCode; inline void deepCopy( const Array<DataType> & original ); The deepCopy function is placed into the private section of the Array class template. 83 The deepCopy Function is Private (cont.) private: DataType *elements; int capacity; DataType dud; int errorCode; inline void deepCopy( const Array<DataType> & original ); It will only be called by the copy constructor and the overloaded assignment operator (the client cannot call it). 84 Nuts and Bolts of the deepCopy Function from copy constructor: 1 2 3 4 5 6 7 8 9 10 deepCopy( ap ); template <class DataType> inline void Array<DataType>::deepCopy( const Array<DataType> & original ) { capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity]; for ( int i = 0; i < capacity; i++ ) elements[ i ] = original.elements[ i ]; } 85 Nuts and Bolts of the deepCopy Function (cont.) 1 2 3 4 5 6 7 8 9 10 template <class DataType> inline void Array<DataType>::deepCopy( const Array<DataType> & original ) { capacity = original.capacity; capacity of errorCode = original.errorCode; arr2 (in our elements = new DataType [capacity]; example) for ( int i = 0; i < capacity; i++ ) elements[ i ] = original.elements[ i ]; } 86 Nuts and Bolts of the deepCopy Function (cont.) 1 2 3 4 5 6 7 8 9 10 template <class DataType> inline void Array<DataType>::deepCopy( const Array<DataType> & original ) { capacity = original.capacity; capacity of errorCode = original.errorCode; arr (in our elements = new DataType [capacity]; example) for ( int i = 0; i < capacity; i++ ) elements[ i ] = original.elements[ i ]; } 87 Nuts and Bolts of the deepCopy Function (cont.) 1 2 3 4 5 6 7 8 9 10 template <class DataType> inline void Array<DataType>::deepCopy( const Array<DataType> & original ) { capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity]; for ( int i = 0; i < capacity; i++ ) elements[ i ] = original.elements[ i ]; } 88 Ways That Copy Constructors Are Called When passing an object by value When returning an object by value When an object is initialized in its declaration Array<float> arr = arr2; This does not call the overloaded assignment operator (the overloaded assignment operator is only called when the object on the left is not being declared) The copy constructor is called for arr, and arr2 is passed in as the parameter into the copy constructor 89 Array Object Assignment If an overloaded assignment operator is not written for the Array class template, then assigning one Array object to another will produce a shallow copy of the first their elements pointers will point to the same dynamic array An overloaded assignment operator would be written for a deep copy calls our deepCopy function 90 Array Object Assignment (cont.) But some issues exist in assignment that don't exist in parameter passing Consider... 91 Array Object Assignment (cont.) arr int errorCode: 0 int capacity: 5 int *elements: 51030 Given these initial Array objects, what happens in the deepCopy function for arr = arr2; 51030 arr2 25 75 10 12 59 int errorCode: 0 int capacity: 4 int *elements: 41066 41066 25 75 10 12 92 Array Object Assignment (cont.) arr int errorCode: 0 int capacity: 5 int *elements: 51030 arr = arr2; The original is arr2 the deepCopy function is called for arr 51030 arr2 25 75 10 12 59 int errorCode: 0 int capacity: 4 int *elements: 41066 41066 25 75 10 12 93 Array Object Assignment (cont.) arr int errorCode: 0 int capacity: 5 int *elements: 51030 arr = arr2; from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity]; 25 75 10 12 59 51030 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 41066 25 75 10 12 94 Array Object Assignment (cont.) arr int errorCode: 0 int capacity: 5 int *elements: 51030 arr = arr2; from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity]; 25 75 10 12 59 51030 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 41066 25 75 10 12 95 Array Object Assignment (cont.) arr int errorCode: 0 int capacity: 4 int *elements: 51030 arr = arr2; from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity]; 25 75 10 12 59 51030 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 41066 25 75 10 12 96 Array Object Assignment (cont.) arr int errorCode: 0 int capacity: 4 int *elements: 51030 arr = arr2; from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity]; 25 75 10 12 59 51030 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 41066 25 75 10 12 97 Array Object Assignment (cont.) arr int errorCode: 0 int capacity: 4 int *elements: 51030 arr = arr2; from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity]; 25 75 10 12 59 51030 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 41066 25 75 10 12 98 Array Object Assignment (cont.) arr int errorCode: 0 int capacity: 4 int *elements: 51030 arr = arr2; from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity]; 25 75 10 12 59 51030 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 41066 25 75 10 12 99 Array Object Assignment (cont.) arr int errorCode: 0 int capacity: 4 int *elements: 64599 arr = arr2; from deepCopy: capacity = original.capacity; 64599 errorCode = original.errorCode; elements = new DataType [capacity]; 25 75 10 12 59 51030 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 41066 25 75 10 12 100 Array Object Assignment (cont.) arr int errorCode: 0 int capacity: 4 int *elements: 64599 MEMORY LEAK! arr2 arr = arr2; from deepCopy: capacity = original.capacity; 64599 errorCode = original.errorCode; elements = new DataType [capacity]; 25 75 10 12 59 51030 41066 25 75 10 12 int errorCode: 0 int capacity: 4 int *elements: 41066 101 Array Object Assignment (cont.) To solve this problem, we must free the elements array in arr first, before calling deepCopy... 102 Array Object Assignment (cont.) template <class DataType> .................... ::operator =( const Array<DataType> & right ) { . . delete [ ] elements; deepCopy( right ); . . } 103 Array Object Assignment (cont.) template <class DataType> .................... ::operator =( const Array<DataType> & right ) { . . But we still need to put in delete [ ] elements; some missing pieces... deepCopy( right ); . . } 104 Multiple Assignment In normal types of assignment, we can do this: x = y = z = 0; so we should also be able to do this: arr1 = arr2 = arr3; 105 How Multiple Assignment Works arr1 = arr2 = arr3; 106 How Multiple Assignment Works (cont.) arr1 = arr2 = arr3; Evaluated first assignment is right associate The overloaded assignment operator is called for arr2 it also returns arr2 when completed Then arr2 replaces this expression, just like the expression 3 + 4 is replaced by 7 107 How Multiple Assignment Works (cont.) arr1 = arr2; After the replacement, the next assignment can be done. This would not be possible if arr2 were not returned from the overloaded assignment operator function. 108 How Multiple Assignment Works (cont.) template <class DataType> .................... ::operator =( const Array<DataType> & right ) { . But how can this function . return the object that it is delete [ ] elements; called for? deepCopy( right ); . . } 109 this A correctly written overloaded assignment operator function uses a this pointer in a couple of places The keyword this can be used in function definitions for structs and classes It is a pointer to the object that owns the function being executed 110 Return *this template <class DataType> .................... ::operator =( const Array<DataType> & right ) { . . delete [ ] elements; deepCopy( right ); return *this; } 111 Return *this (cont.) template <class DataType> .................... ::operator =( const Array<DataType> & right ) { . . delete [ ] elements; Now we can fill in this deepCopy( right ); part... return *this; } 112 Return *this (cont.) template <class DataType> Array<DataType> Array<DataType>::operator =( const Array<DataType> & right ) { . . Now we can fill in this delete [ ] elements; part... deepCopy( right ); return *this; } 113 Return *this (cont.) template <class DataType> Array<DataType> Array<DataType>::operator =( const Array<DataType> & right ) { . . Return type delete [ ] elements; deepCopy( right ); return *this; } 114 Return *this (cont.) template <class DataType> Array<DataType> Array<DataType>::operator =( const Array<DataType> & right ) { . . The usual delete [ ] elements; class name deepCopy( right ); return *this; } 115 Return *this (cont.) template <class DataType> Array<DataType> Array<DataType>::operator =( const Array<DataType> & right ) { . . We can make another delete [ ] elements; improvement by recalling deepCopy( right ); that return by value calls the copy constructor... return *this; } 116 Return *this (cont.) template <class DataType> Array<DataType> Array<DataType>::operator =( const Array<DataType> & right ) { . . so let's speed things up a delete [ ] elements; bit and return by reference deepCopy( right ); return *this; } 117 Return *this (cont.) template <class DataType> Array<DataType> & Array<DataType>::operator =( const Array<DataType> & right ) { . . so let's speed things up a delete [ ] elements; bit and return by reference deepCopy( right ); return *this; } 118 Return *this (cont.) template <class DataType> Array<DataType> & Array<DataType>::operator =( const Array<DataType> & right ) { It is OK to return by . reference in this case, . because the object on the delete [ ] elements; left of the assignment will deepCopy( right ); still be around after we return. (Local objects return *this; should not be returned by } reference why?) 119 Return *this (cont.) template <class DataType> Array<DataType> & Array<DataType>::operator =( const Array<DataType> & right ) { We still have one more . piece of the puzzle . delete [ ] elements; deepCopy( right ); return *this; } 120 An Object Assigned to Itself template <class DataType> Array<DataType> & Array<DataType>::operator =( const Array<DataType> & right ) { Consider an array of Array . objects an Array object . assignment can be done delete [ ] elements; like this: deepCopy( right ); arr[ i ] = arr[ j ]; return *this; What happens if i == j? } 121 An Object Assigned to Itself (cont.) template <class DataType> Array<DataType> & Array<DataType>::operator =( const Array<DataType> & right ) { arr[ i ] = arr[ j ]; . . What happens if i == j? delete [ ] elements; The same Array object is deepCopy( right ); assigned to itself! (This can happen in some return *this; sorting algorithms.) } 122 An Object Assigned to Itself (cont.) template <class DataType> Array<DataType> & Array<DataType>::operator =( const Array<DataType> & right ) { What happens if an Array . object is assigned to itself . here? delete [ ] elements; deepCopy( right ); return *this; } 123 An Object Assigned to Itself (cont.) template <class DataType> Array<DataType> & Array<DataType>::operator =( const Array<DataType> & right ) { This line frees the dynamic . array of the Array object . on the left of the delete [ ] elements; assignment, and deepCopy( right ); (consequently) the dynamic array of the return *this; object on the right of the } assignment! 124 An Object Assigned to Itself (cont.) template <class DataType> Array<DataType> & Array<DataType>::operator =( const Array<DataType> & right ) { Tests to see if the if ( this == &right ) address of the object return *this; on the left has the same address as the delete [ ] elements; object on the right. deepCopy( right ); return *this; } 125 An Object Assigned to Itself (cont.) template <class DataType> Array<DataType> & Array<DataType>::operator =( const Array<DataType> & right ) { If they are the same if ( this == &right ) object, return right return *this; away. delete [ ] elements; deepCopy( right ); return *this; } 126 Complete Overloaded Assignment Operator template <class DataType> Array<DataType> & Array<DataType>::operator =( const Array<DataType> & right ) { if ( this == &right ) return *this; delete [ ] elements; deepCopy( right ); return *this; } 127 Guidelines for Writing Overloaded Assignment Operators Check to see if the objects on both sides of the assignment are the same; if so, just return *this Free the dynamic memory pointed to by all pointers in the object on the left Call a deepCopy function Return *this whenever you need to return 128 To Summarize When a class has a pointer data member that will point to dynamic memory, you need to write three functions for the class: a destructor a copy constructor an overloaded assignment operator 129 ...
View Full Document

Ask a homework question - tutors are online