Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 5 An Array Class Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall.

Similar presentations


Presentation on theme: "1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 5 An Array Class Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall."— Presentation transcript:

1 1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 5 An Array Class Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall

2 2 Before We Begin… The Array class template uses some concepts you may not have seen before –inline –return by reference –bitwise operators & and |

3 3 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

4 4 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

5 5 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;

6 6 Bitwise AND 101110 111000 101000 c = a & b;

7 7 Bitwise OR 101110 111000 111110 c = a | b;

8 8 An Array Class Template 1 // Array.h -- class template for an adjustable array 2 // When debugging, use #define DEBUG_ARRAY above 3 // your #include Array line. When done debugging, 4 // comment out #define DEBUG_ARRAY for better 5 // performance. 6 // The constructor and the changeSize function can cause 7 //an exception to be thrown if out of heap memory. 6 7 #include 8 9 using namespace std;

9 9 An Array Class Template (cont.) 10 template 11 class Array 12 { 13 public: 14Array( int size ); 15inline DataType & operator [ ]( int index ); 16void changeSize( int newSize ); // will not alter values 17// unless newSize is smaller than current capacity; 18// in this case, the values from 0 to newSize - 1 19// will not be altered 19inline int length( ) const;// returns current capacity 20string err( ) const; // returns error message

10 10 An Array Class Template (cont.) 21 private: 22DataType *elements; // points to the dynamic array 23int capacity; 24DataType dud; // returned from operator [ ] if 25 // index error occurs 26int errorCode; // contains code for error if array 27 // misuse occurs 28 }; 29 30 #include "Array.cpp"

11 11 An Array Class Template (cont.) 1 // Array.cpp -- function definitions for an array 2 3 // Error codes -- use powers of 2 4 // 0No error. 5 // 1Nonpositive size passed into constructor. 6 // 2Invalid index was used. 7 // 4Nonpositive new size passed into changeSize 8 //function 1 = 00012 = 00104 = 0100

12 12 An Array Class Template (cont.) 1 // Array.cpp -- function definitions for an array 2 3 // Error codes -- use powers of 2 4 // 0No error. 5 // 1Nonpositive size passed into constructor. 6 // 2Invalid index was used. 7 // 4Nonpositive new size passed into changeSize 8 //function Example: Error code 2 can be recorded with: errorCode |= 2;

13 13 An Array Class Template (cont.) 1 // Array.cpp -- function definitions for an array 2 3 // Error codes -- use powers of 2 4 // 0No error. 5 // 1Nonpositive size passed into constructor. 6 // 2Invalid index was used. 7 // 4Nonpositive new size passed into changeSize 8 //function Using this technique a single integer errorCode can be used to record all types of errors that have occurred.

14 14 An Array Class Template (cont.) 1 // Array.cpp -- function definitions for an array 2 3 // Error codes -- use powers of 2 4 // 0No error. 5 // 1Nonpositive size passed into constructor. 6 // 2Invalid index was used. 7 // 4Nonpositive new size passed into changeSize 8 //function An errorCode of 0101 means errors 1 and 4 occurred

15 15 An Array Class Template (cont.) 1 // Array.cpp -- function definitions for an array 2 3 // Error codes -- use powers of 2 4 // 0No error. 5 // 1Nonpositive size passed into constructor. 6 // 2Invalid index was used. 7 // 4Nonpositive new size passed into changeSize 8 //function If error code 2 occurred, it can be detected with: if (errorCode & 2)// false if errorCode is 0101

16 16 An Array Class Template (cont.) 1 // Array.cpp -- function definitions for an array 2 3 // Error codes -- use powers of 2 4 // 0No error. 5 // 1Nonpositive size passed into constructor. 6 // 2Invalid index was used. 7 // 4Nonpositive new size passed into changeSize 8 //function If error code 2 occurred, it can be detected with: if (errorCode & 2)// true if errorCode is 0110

17 17 An Array Class Template (cont.) 9 template 10 Array ::Array( int size ) 11 { 12if ( size < 1 ) { 13capacity = 1; 14errorCode = 1; // nonpositive size 15} 16else { 17capacity = size; 18errorCode = 0; // no error 19} 20 21elements = new DataType [capacity]; 22 }

18 18 An Array Class Template (cont.) 23 template 24 inline DataType & Array ::operator [ ]( int index ) 25 { 26 #ifdef DEBUG_ARRAY 27if ( index = capacity ) { 28errorCode |= 2; // invalid index 29return dud; 30} 31 #endif 32return elements[ index ]; 33 } Conditional Compilation

19 19 An Array Class Template (cont.) 34 // will not alter values unless newSize is smaller than 35 // current capacity; in this case, the values from 0 to 36 // newSize - 1 will not be altered 37 template 38 void Array ::changeSize( int newSize ) 39 { 40if ( newSize < 1 ) 41{ 42errorCode |= 4; // nonpositive new size 43return; 44} changeSize function continued…

20 20 An Array Class Template (cont.) 45DataType *newArray = new DataType [newSize]; 46int limit = (newSize > capacity)? capacity : newSize; 47 48for ( int i = 0; i < limit; i++ ) 49newArray[ i ] = elements[ i ]; 50 51delete [ ] elements; 52elements = newArray; 53 54capacity = newSize; 55 } ternary operator

21 21 An Array Class Template (cont.) 45DataType *newArray = new DataType [newSize]; 46int limit = (newSize > capacity)? capacity : newSize; 47 48for ( int i = 0; i < limit; i++ ) 49newArray[ i ] = elements[ i ]; 50 51delete [ ] elements; 52elements = newArray; 53 54capacity = newSize; 55 } if (newSize > capacity ) limit = capacity; else limit = newSize;

22 22 An Array Class Template (cont.) 56 template 57 inline int Array ::length( ) const 58 { 59return capacity; 60 }

23 23 An Array Class Template (cont.) 61 template 62 string Array ::err( ) const 63 { 64 65if ( errorCode == 0 ) 66return "No error.\n"; err function continued…

24 24 An Array Class Template (cont.) 67string errorMessage = ""; 68if ( errorCode & 1 ) { // nonpositive size 69errorMessage += 70"Nonpositive size passed into constructor, so\n"; 71errorMessage += 72"the capacity was set to 1 by default.\n"; 73} 74if ( errorCode & 2 ) // invalid index 75errorMessage += "Index out of range.\n"; err function continued…

25 25 An Array Class Template (cont.) 76if ( errorCode & 4 ) { // nonpositive new size in 77 // changeSize 78errorMessage += 79 "Nonpositive size passed into changeSize, so\n"; 80errorMessage += 81"the size of the array was not changed.\n"; 82} 83 84return errorMessage; 85 }

26 26 Using the Array Class Template 1 // useArray.cpp -- a program that demonstrates the use of 2 // the Array class 3 4 #include 5 #define DEBUG_ARRAY 6 #include "Array.h" 7 8 using namespace std; 9 10 void getElements( Array & numbers ); 11 12 float calcAverage( Array avnums ); To uncover problems

27 27 Using the Array Class Template (cont.) 13 int main( ) 14 { 15Array nums( 2 ); 16 17getElements( nums ); 18float average = calcAverage( nums ); 19 20cout << "The average is: " << average << endl; 21 22return 0; 23 }

28 28 Using the Array Class Template (cont.) 24 void getElements( Array & numbers ) 25 { 26int i = 0; 27 28cout << "Enter a positive integer: "; 29cin >> numbers[ i ]; 30while ( numbers[ i ] != -1 ) { 31i++; 32if ( i == numbers.length( ) ) 33numbers.changeSize( i * 2 ); 34cout << "Enter a positive integer (enter -1 to stop): "; 35cin >> numbers[ i ]; 36} getElements cont…

29 29 Using the Array Class Template (cont.) 37numbers.changeSize( i ); 38 39cout << "getElements: " << numbers.err( ); 40 } sets the capacity to the exact number of elements entered.

30 30 Using the Array Class Template (cont.) 37numbers.changeSize( i ); 38 39cout << "getElements: " << numbers.err( ); 40 } Used for debugging purposes – to uncover errors in the getElements function

31 31 Using the Array Class Template (cont.) 41 float calcAverage( Array avnums ) 42 { 43int sum = 0; 44for ( int i = 0; i <= avnums.length( ); i++ ) 45sum += avnums[ i ]; 46 47cout << "calcAverage: " << avnums.err( ); 48return sum / float( avnums.length( ) ); 49 } error

32 32 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]

33 33 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

34 34 After Debugging… (cont.) 1 // useArray.cpp -- a program that demonstrates the use of 2 // the Array class 3 4 #include 5 #define DEBUG_ARRAY 6 #include "Array.h" 7 8 using namespace std; 9 10 void getElements( Array & numbers ); 11 12 float calcAverage( Array avnums );

35 35 After Debugging… (cont.) 1 // useArray.cpp -- a program that demonstrates the use of 2 // the Array class 3 4 #include 5 // #define DEBUG_ARRAY 6 #include "Array.h" 7 8 using namespace std; 9 10 void getElements( Array & numbers ); 11 12 float calcAverage( Array avnums );

36 36 After Debugging… (cont.) 1 // useArray.cpp -- a program that demonstrates the use of 2 // the Array class 3 4 #include 5 // #define DEBUG_ARRAY 6 #include "Array.h" 7 8 using namespace std; 9 10 void getElements( Array & numbers ); 11 12 float calcAverage( Array avnums ); for better array performance

37 37 After Debugging… (cont.) 37numbers.changeSize( i ); 38 39cout << "getElements: " << numbers.err( ); 40 } end of getElements

38 38 After Debugging… (cont.) 37numbers.changeSize( i ); 38 39// cout << "getElements: " << numbers.err( ); 40 } end of getElements

39 39 After Debugging… (cont.) 41 float calcAverage( Array avnums ) 42 { 43int sum = 0; 44for ( int i = 0; i < avnums.length( ); i++ ) 45sum += avnums[ i ]; 46 47cout << "calcAverage: " << avnums.err( ); 48return sum / float( avnums.length( ) ); 49 }

40 40 After Debugging… (cont.) 41 float calcAverage( Array avnums ) 42 { 43int sum = 0; 44for ( int i = 0; i < avnums.length( ); i++ ) 45sum += avnums[ i ]; 46 47// cout << "calcAverage: " << avnums.err( ); 48return sum / float( avnums.length( ) ); 49 }

41 41 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.

42 42 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( );

43 43 template Array ::~Array( ) { delete [ ] elements; } Destructor in Class Implementation File

44 44 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)

45 45 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 *ptr = new Array (10); ….// other code ….. delete ptr; Calls the destructor.

46 46 Problems With Passing Parameters foo( arr );..// other code. void foo( Array arr2 ) {.. // function code. } arr, an Array object, is passed by value into function foo

47 47 Problems With Passing Parameters (cont.) foo( arr );..// other code. void foo( Array 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

48 48 Problems With Passing Parameters (cont.) foo( arr );..// other code. void foo( Array arr2 ) {.. // function code. } arr int errorCode: 0 int capacity: 4 int *elements: 51030 The address stored in elements is the address of a dynamic array with 4 elements

49 49 Problems With Passing Parameters (cont.) foo( arr );..// other code. void foo( Array arr2 ) {.. // function code. } arr int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012

50 50 Problems With Passing Parameters (cont.) foo( arr );..// other code. void foo( Array arr2 ) {.. // function code. } arr int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 When arr is passed into function foo, a copy of each data member’s value is made.

51 51 Problems With Passing Parameters (cont.) foo( arr );..// other code. void foo( Array arr2 ) { } arr int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 51030 But then the elements pointer in arr2 points to the same array!

52 52 Problems With Passing Parameters (cont.) foo( arr );..// other code. void foo( Array arr2 ) { } arr int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 51030 Problem 1: Changes in the array are reflected back to the caller.

53 53 Problems With Passing Parameters (cont.) foo( arr );..// other code. void foo( Array arr2 ) { } arr int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 51030 (this isn’t what we want in pass by value!)

54 54 Problems With Passing Parameters (cont.) foo( arr );..// other code. void foo( Array arr2 ) { } arr int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 51030 When foo finishes execution, arr2 will be destroyed…

55 55 Problems With Passing Parameters (cont.) foo( arr );..// other code. void foo( Array arr2 ) { } arr int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 51030 calling the destructor for arr2 and freeing the dynamic array!

56 56 Problems With Passing Parameters (cont.) foo( arr );..// other code. void foo( Array arr2 ) { } arr int errorCode: 0 int capacity: 4 int *elements: 51030

57 57 Problems With Passing Parameters (cont.) foo( arr );..// other code. void foo( Array arr2 ) { } arr int errorCode: 0 int capacity: 4 int *elements: 51030 One can safely say that this isn’t what we want either! (Problem 2)

58 58 Problems With Passing Parameters (cont.) foo( arr );..// other code. void foo( Array arr2 ) { } arr int errorCode: 0 int capacity: 4 int *elements: 51030 This is called a dangling pointer or a dangling reference.

59 59 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.

60 60 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 & ap ); The copy constructor is called for the formal parameter (arr2 in our example)

61 61 Copy Constructor – the Solution (cont.) Array( const Array & ap ); The object passed into the copy constructor is the actual parameter in the function call (arr in our example)

62 62 Copy Constructor – the Solution (cont.) Array( const Array & 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.

63 63 Copy Constructor – the Solution (cont.) Array( const Array & ap ); It is passed by const reference since a change to the actual parameter should not occur (the compiler would catch it)

64 64 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

65 65 Shallow Copy foo( arr );..// other code. void foo( Array arr2 ) { } arr int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 51030

66 66 Deep Copy foo( arr );..// other code. void foo( Array arr2 ) { } arr int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066

67 67 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

68 68 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

69 69 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.

70 70 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

71 71 The Copy Constructor Definition for Array template Array ::Array( const Array & ap ) { deepCopy( ap ); }

72 72 The Copy Constructor Process foo( arr );..// other code. void foo( Array arr2 ) {.. // function code. } First, the foo function call is made…

73 73 The Copy Constructor Process (cont.) foo( arr );..// other code. void foo( Array 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)

74 74 The Copy Constructor Process (cont.) foo( arr );..// other code. void foo( Array arr2 ) template Array ::Array( const Array & ap ) { deepCopy( ap ); } The copy constructor for arr2 executes

75 75 The Copy Constructor Process (cont.) foo( arr );..// other code. void foo( Array arr2 ) template Array ::Array( const Array & ap ) { deepCopy( ap ); } arr is passed as the parameter into the copy constructor function heading

76 76 The Copy Constructor Process (cont.) foo( arr );..// other code. void foo( Array arr2 ) template Array ::Array( const Array & ap ) { deepCopy( ap ); } the deepCopy function executes using ap (arr) as the original

77 77 ap (or arr) int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 The Copy Constructor Process (cont.) The deepCopy function produces a deep copy of the ap (or arr) Array object, for arr2.

78 78 int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066 The Copy Constructor Process (cont.) The deepCopy function produces a deep copy of the ap (or arr) Array object, for arr2. ap (or arr)

79 79 The Copy Constructor Process (cont.) foo( arr );..// other code. void foo( Array arr2 ) template Array ::Array( const Array & ap ) { deepCopy( ap ); } the deepCopy function returns, then the copy constructor returns.

80 80 The Copy Constructor Process (cont.) foo( arr );..// other code. void foo( Array arr2 ) arr2 now has a deep copy of the arr object (parameter passing has completed)

81 81 foo( arr );..// other code. void foo( Array arr2 ) { } arr int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066 The Copy Constructor Process (cont.)

82 82 The Copy Constructor Process (cont.) foo( arr );..// other code. void foo( Array arr2 ) {.. // function code. } The foo function executes.

83 83 The deepCopy Function is Private private: DataType *elements; int capacity; DataType dud; int errorCode; inline void deepCopy( const Array & original ); The deepCopy function is placed into the private section of the Array class template.

84 84 The deepCopy Function is Private (cont.) private: DataType *elements; int capacity; DataType dud; int errorCode; inline void deepCopy( const Array & original ); It will only be called by the copy constructor and the overloaded assignment operator (the client cannot call it).

85 85 Nuts and Bolts of the deepCopy Function 1 template 2 inline void Array ::deepCopy( 3 const Array & original ) 4 { 5capacity = original.capacity; 6errorCode = original.errorCode; 7elements = new DataType [capacity]; 8for ( int i = 0; i < capacity; i++ ) 9elements[ i ] = original.elements[ i ]; 10 } from copy constructor: deepCopy( ap );

86 86 Nuts and Bolts of the deepCopy Function (cont.) 1 template 2 inline void Array ::deepCopy( 3 const Array & original ) 4 { 5capacity = original.capacity; 6errorCode = original.errorCode; 7elements = new DataType [capacity]; 8for ( int i = 0; i < capacity; i++ ) 9elements[ i ] = original.elements[ i ]; 10 } capacity of arr2 (in our example)

87 87 Nuts and Bolts of the deepCopy Function (cont.) 1 template 2 inline void Array ::deepCopy( 3 const Array & original ) 4 { 5capacity = original.capacity; 6errorCode = original.errorCode; 7elements = new DataType [capacity]; 8for ( int i = 0; i < capacity; i++ ) 9elements[ i ] = original.elements[ i ]; 10 } capacity of arr (in our example)

88 88 Nuts and Bolts of the deepCopy Function (cont.) 1 template 2 inline void Array ::deepCopy( 3 const Array & original ) 4 { 5capacity = original.capacity; 6errorCode = original.errorCode; 7elements = new DataType [capacity]; 8for ( int i = 0; i < capacity; i++ ) 9elements[ i ] = original.elements[ i ]; 10 }

89 89 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 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

90 90 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

91 91 Array Object Assignment (cont.) But some issues exist in assignment that don’t exist in parameter passing Consider…

92 92 int errorCode: 0 int capacity: 5 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066 Array Object Assignment (cont.) arr 59 Given these initial Array objects, what happens in the deepCopy function for arr = arr2;

93 93 int errorCode: 0 int capacity: 5 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066 Array Object Assignment (cont.) arr 59 arr = arr2; The original is arr2 – the deepCopy function is called for arr

94 94 int errorCode: 0 int capacity: 5 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066 Array Object Assignment (cont.) arr 59 arr = arr2; from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity];

95 95 int errorCode: 0 int capacity: 5 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066 Array Object Assignment (cont.) arr 59 arr = arr2; from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity];

96 96 int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066 Array Object Assignment (cont.) arr 59 arr = arr2; from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity];

97 97 int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066 Array Object Assignment (cont.) arr 59 arr = arr2; from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity];

98 98 int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066 Array Object Assignment (cont.) arr 59 arr = arr2; from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity];

99 99 int errorCode: 0 int capacity: 4 int *elements: 51030 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066 Array Object Assignment (cont.) arr 59 arr = arr2; from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity];

100 100 from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity]; int errorCode: 0 int capacity: 4 int *elements: 64599 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066 Array Object Assignment (cont.) arr 59 64599 arr = arr2;

101 101 from deepCopy: capacity = original.capacity; errorCode = original.errorCode; elements = new DataType [capacity]; int errorCode: 0 int capacity: 4 int *elements: 64599 25 51030 751012 arr2 int errorCode: 0 int capacity: 4 int *elements: 41066 25751012 41066 Array Object Assignment (cont.) arr 59 64599 MEMORY LEAK! arr = arr2;

102 102 Array Object Assignment (cont.) To solve this problem, we must free the elements array in arr first, before calling deepCopy…

103 103 Array Object Assignment (cont.) template ……………….. ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right );. }

104 104 Array Object Assignment (cont.) template ……………….. ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right );. } But we still need to put in some missing pieces…

105 105 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;

106 106 arr1 = arr2 = arr3; How Multiple Assignment Works

107 107 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 How Multiple Assignment Works (cont.)

108 108 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. How Multiple Assignment Works (cont.)

109 109 template ……………….. ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right );. } But how can this function return the object that it is called for? How Multiple Assignment Works (cont.)

110 110 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

111 111 template ……………….. ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } Return *this

112 112 template ……………….. ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } Return *this (cont.) Now we can fill in this part…

113 113 template Array Array ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } Return *this (cont.) Now we can fill in this part…

114 114 template Array Array ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } Return *this (cont.) Return type

115 115 template Array Array ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } Return *this (cont.) The usual class name

116 116 template Array Array ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } Return *this (cont.) We can make another improvement by recalling that return by value calls the copy constructor…

117 117 template Array Array ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } Return *this (cont.) so let’s speed things up a bit and return by reference

118 118 template Array & Array ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } Return *this (cont.) so let’s speed things up a bit and return by reference

119 119 template Array & Array ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } Return *this (cont.) It is OK to return by reference in this case, because the object on the left of the assignment will still be around after we return. (Local objects should not be returned by reference – why?)

120 120 template Array & Array ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } Return *this (cont.) We still have one more piece of the puzzle

121 121 template Array & Array ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } An Object Assigned to Itself Consider an array of Array objects – an Array object assignment can be done like this: arr[ i ] = arr[ j ]; What happens if i == j?

122 122 template Array & Array ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } An Object Assigned to Itself (cont.) arr[ i ] = arr[ j ]; What happens if i == j? The same Array object is assigned to itself! (This can happen in some sorting algorithms.)

123 123 template Array & Array ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } An Object Assigned to Itself (cont.) What happens if an Array object is assigned to itself here?

124 124 template Array & Array ::operator =( const Array & right ) {. delete [ ] elements; deepCopy( right ); return *this; } An Object Assigned to Itself (cont.) This line frees the dynamic array of the Array object on the left of the assignment, and (consequently) the dynamic array of the object on the right of the assignment!

125 125 template Array & Array ::operator =( const Array & right ) { if ( this == &right ) return *this; delete [ ] elements; deepCopy( right ); return *this; } Tests to see if the address of the object on the left has the same address as the object on the right. An Object Assigned to Itself (cont.)

126 126 template Array & Array ::operator =( const Array & right ) { if ( this == &right ) return *this; delete [ ] elements; deepCopy( right ); return *this; } An Object Assigned to Itself (cont.) If they are the same object, return right away.

127 127 template Array & Array ::operator =( const Array & right ) { if ( this == &right ) return *this; delete [ ] elements; deepCopy( right ); return *this; } Complete Overloaded Assignment Operator

128 128 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

129 129 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


Download ppt "1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 5 An Array Class Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall."

Similar presentations


Ads by Google