1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 5 An Array Class Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall
Jan 03, 2016
1
C++ Classes and Data StructuresJeffrey S. Childs
Chapter 5An Array Class
Jeffrey S. Childs
Clarion University of PA
© 2008, Prentice Hall
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
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
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
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
Bitwise AND
101110
111000101000
c = a & b;
7
Bitwise OR
101110
111000111110
c = a | b;
8
An Array Class Template
1 // Array.h -- class template for an adjustable array2 // 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. 67 #include <string>89 using namespace std;
9
An Array ClassTemplate (cont.)
10 template <class DataType>11 class Array12 {13 public:14 Array( int size );15 inline DataType & operator [ ]( int index ); 16 void 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 altered19 inline int length( ) const; // returns current capacity 20 string err( ) const; // returns error message
10
An Array ClassTemplate (cont.)
21 private:22 DataType *elements; // points to the dynamic array23 int capacity; 24 DataType dud; // returned from operator [ ] if 25 // index error occurs26 int errorCode; // contains code for error if array 27 // misuse occurs28 };2930 #include "Array.cpp"
11
An Array ClassTemplate (cont.)
1 // Array.cpp -- function definitions for an array23 // Error codes -- use powers of 24 // 0 No error.5 // 1 Nonpositive size passed into constructor.6 // 2 Invalid index was used.7 // 4 Nonpositive new size passed into changeSize 8 // function
1 = 0001 2 = 0010 4 = 0100
12
An Array ClassTemplate (cont.)
1 // Array.cpp -- function definitions for an array23 // Error codes -- use powers of 24 // 0 No error.5 // 1 Nonpositive size passed into constructor.6 // 2 Invalid index was used.7 // 4 Nonpositive new size passed into changeSize 8 // function
Example: Error code 2 can be recorded with:errorCode |= 2;
13
An Array ClassTemplate (cont.)
1 // Array.cpp -- function definitions for an array23 // Error codes -- use powers of 24 // 0 No error.5 // 1 Nonpositive size passed into constructor.6 // 2 Invalid index was used.7 // 4 Nonpositive 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
An Array ClassTemplate (cont.)
1 // Array.cpp -- function definitions for an array23 // Error codes -- use powers of 24 // 0 No error.5 // 1 Nonpositive size passed into constructor.6 // 2 Invalid index was used.7 // 4 Nonpositive new size passed into changeSize 8 // function
An errorCode of 0101 means errors 1 and 4 occurred
15
An Array ClassTemplate (cont.)
1 // Array.cpp -- function definitions for an array23 // Error codes -- use powers of 24 // 0 No error.5 // 1 Nonpositive size passed into constructor.6 // 2 Invalid index was used.7 // 4 Nonpositive 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
An Array ClassTemplate (cont.)
1 // Array.cpp -- function definitions for an array23 // Error codes -- use powers of 24 // 0 No error.5 // 1 Nonpositive size passed into constructor.6 // 2 Invalid index was used.7 // 4 Nonpositive 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
An Array Class Template (cont.)9 template <class DataType>10 Array<DataType>::Array( int size )11 {12 if ( size < 1 ) {13 capacity = 1;14 errorCode = 1; // nonpositive size15 }16 else {17 capacity = size;18 errorCode = 0; // no error19 }2021 elements = new DataType [capacity];22 }
18
An Array ClassTemplate (cont.)
23 template <class DataType>24 inline DataType & Array<DataType>::operator [ ]( int index )25 {26 #ifdef DEBUG_ARRAY27 if ( index < 0 || index >= capacity ) {28 errorCode |= 2; // invalid index29 return dud;30 }31 #endif32 return elements[ index ];33 }
Conditional Compilation
19
An Array ClassTemplate (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 altered37 template <class DataType>38 void Array<DataType>::changeSize( int newSize )39 {40 if ( newSize < 1 )41 {42 errorCode |= 4; // nonpositive new size 43 return;44 } changeSize function continued…
20
An Array ClassTemplate (cont.)
45 DataType *newArray = new DataType [newSize];46 int limit = (newSize > capacity)? capacity : newSize;4748 for ( int i = 0; i < limit; i++ )49 newArray[ i ] = elements[ i ];5051 delete [ ] elements;52 elements = newArray;5354 capacity = newSize;55 }
ternary operator
21
An Array ClassTemplate (cont.)
45 DataType *newArray = new DataType [newSize];46 int limit = (newSize > capacity)? capacity : newSize;4748 for ( int i = 0; i < limit; i++ )49 newArray[ i ] = elements[ i ];5051 delete [ ] elements;52 elements = newArray;5354 capacity = newSize;55 }
if (newSize > capacity )limit = capacity;
elselimit = newSize;
22
An Array ClassTemplate (cont.)
56 template <class DataType>57 inline int Array<DataType>::length( ) const58 {59 return capacity;60 }
23
An Array ClassTemplate (cont.)
61 template <class DataType>62 string Array<DataType>::err( ) const63 {6465 if ( errorCode == 0 )66 return "No error.\n";
err function continued…
24
An Array ClassTemplate (cont.)
67 string errorMessage = "";68 if ( errorCode & 1 ) { // nonpositive size69 errorMessage += 70 "Nonpositive size passed into constructor, so\n";71 errorMessage += 72 "the capacity was set to 1 by default.\n";73 }74 if ( errorCode & 2 ) // invalid index75 errorMessage += "Index out of range.\n";
err function continued…
25
An Array ClassTemplate (cont.)
76 if ( errorCode & 4 ) { // nonpositive new size in 77 // changeSize78 errorMessage += 79 "Nonpositive size passed into changeSize, so\n";80 errorMessage += 81 "the size of the array was not changed.\n";82 }8384 return errorMessage;85 }
26
Using the Array Class Template
1 // useArray.cpp -- a program that demonstrates the use of 2 // the Array class34 #include <iostream>5 #define DEBUG_ARRAY6 #include "Array.h"78 using namespace std;910 void getElements( Array<int> & numbers );1112 float calcAverage( Array<int> avnums );
To uncover problems
27
Using the Array Class Template (cont.)
13 int main( )14 {15 Array<int> nums( 2 );1617 getElements( nums );18 float average = calcAverage( nums );1920 cout << "The average is: " << average << endl;2122 return 0;23 }
28
Using the Array Class Template (cont.)
24 void getElements( Array<int> & numbers )25 {26 int i = 0;2728 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 ];36 }
getElements cont…
29
Using the Array Class Template (cont.)
37 numbers.changeSize( i );3839 cout << "getElements: " << numbers.err( );40 }
sets the capacity to the exact number of elements entered.
30
Using the Array Class Template (cont.)
37 numbers.changeSize( i );3839 cout << "getElements: " << numbers.err( );40 }
Used for debugging purposes – to uncover errors in the getElements function
31
Using the Array Class Template (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 ];4647 cout << "calcAverage: " << avnums.err( );48 return sum / float( avnums.length( ) );49 }
error
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
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
After Debugging…(cont.)
1 // useArray.cpp -- a program that demonstrates the use of 2 // the Array class34 #include <iostream>5 #define DEBUG_ARRAY6 #include "Array.h"78 using namespace std;910 void getElements( Array<int> & numbers );1112 float calcAverage( Array<int> avnums );
35
After Debugging…(cont.)
1 // useArray.cpp -- a program that demonstrates the use of 2 // the Array class34 #include <iostream>5 // #define DEBUG_ARRAY6 #include "Array.h"78 using namespace std;910 void getElements( Array<int> & numbers );1112 float calcAverage( Array<int> avnums );
36
After Debugging…(cont.)
1 // useArray.cpp -- a program that demonstrates the use of 2 // the Array class34 #include <iostream>5 // #define DEBUG_ARRAY6 #include "Array.h"78 using namespace std;910 void getElements( Array<int> & numbers );1112 float calcAverage( Array<int> avnums );
for better array performance
37
After Debugging… (cont.)
37 numbers.changeSize( i );3839 cout << "getElements: " << numbers.err( );40 }
end of getElements
38
After Debugging… (cont.)
37 numbers.changeSize( i );3839 // cout << "getElements: " << numbers.err( );40 }
end of getElements
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 ];4647 cout << "calcAverage: " << avnums.err( );48 return sum / float( avnums.length( ) );49 }
40
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 ];4647 // cout << "calcAverage: " << avnums.err( );48 return sum / float( avnums.length( ) );49 }
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
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
template <class DataType>Array<DataType>::~Array( ){
delete [ ] elements;}
Destructor in Class Implementation File
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
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.
46
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
47
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
48
Problems With Passing Parameters (cont.)
foo( arr );.. // other code...void foo( Array<int> 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
Problems With Passing Parameters (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 ){
.
. // function code
.}
arr int errorCode: 0 int capacity: 4 int *elements: 51030
2551030 75 10 12
50
Problems With Passing Parameters (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 ){
.
. // function code
.}
arr int errorCode: 0 int capacity: 4 int *elements: 51030
2551030 75 10 12
When arr is passed into function foo, a copy of each data member’s value is made.
51
Problems With Passing Parameters (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 ){
}
arr int errorCode: 0 int capacity: 4 int *elements: 51030
2551030 75 10 12
arr2 int errorCode: 0 int capacity: 4 int *elements: 51030
But then the elements pointer in arr2 points to the same array!
52
Problems With Passing Parameters (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 ){
}
arr int errorCode: 0 int capacity: 4 int *elements: 51030
2551030 75 10 12
arr2 int errorCode: 0 int capacity: 4 int *elements: 51030
Problem 1: Changes in the array are reflected back to the caller.
53
Problems With Passing Parameters (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 ){
}
arr int errorCode: 0 int capacity: 4 int *elements: 51030
2551030 75 10 12
arr2 int errorCode: 0 int capacity: 4 int *elements: 51030
(this isn’t what we want in pass by value!)
54
Problems With Passing Parameters (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 ){
}
arr int errorCode: 0 int capacity: 4 int *elements: 51030
2551030 75 10 12
arr2 int errorCode: 0 int capacity: 4 int *elements: 51030
When foo finishes execution, arr2 will be destroyed…
55
Problems With Passing Parameters (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 ){
}
arr int errorCode: 0 int capacity: 4 int *elements: 51030
2551030 75 10 12
arr2 int errorCode: 0 int capacity: 4 int *elements: 51030
calling the destructor for arr2 and freeing the dynamic array!
56
Problems With Passing Parameters (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 ){
}
arr int errorCode: 0 int capacity: 4 int *elements: 51030
57
Problems With Passing Parameters (cont.)
foo( arr );.. // other code...void foo( Array<int> 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
Problems With Passing Parameters (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 ){
}
arr int errorCode: 0 int capacity: 4 int *elements: 51030
This is called a dangling pointer or a dangling reference.
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
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)
61
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)
62
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.
63
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)
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
Shallow Copy
foo( arr );.. // other code...void foo( Array<int> arr2 ){
}
arr int errorCode: 0 int capacity: 4 int *elements: 51030
2551030 75 10 12
arr2 int errorCode: 0 int capacity: 4 int *elements: 51030
66
Deep Copy
foo( arr );.. // other code...void foo( Array<int> arr2 ){
}
arr int errorCode: 0 int capacity: 4 int *elements: 51030
2551030 75 10 12
arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
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
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
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
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
The Copy Constructor Definition for Array
template <class DataType>Array<DataType>::Array( const Array<DataType> & ap ){
deepCopy( ap );}
72
The Copy ConstructorProcess
foo( arr );.. // other code...void foo( Array<int> arr2 ){
.
. // function code
.}
First, the foo function call is made…
73
The Copy ConstructorProcess (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)
74
The Copy ConstructorProcess (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 )
template <class DataType>Array<DataType>::Array( const Array<DataType> & ap ){
deepCopy( ap );}
The copy constructor for arr2 executes
75
The Copy ConstructorProcess (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 )
template <class DataType>Array<DataType>::Array( const Array<DataType> & ap ){
deepCopy( ap );}
arr is passed as the parameter into the copy constructor function heading
76
The Copy ConstructorProcess (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 )
template <class DataType>Array<DataType>::Array( const Array<DataType> & ap ){
deepCopy( ap );}
the deepCopy function executes using ap (arr) as the original
77
ap (or arr) int errorCode: 0 int capacity: 4 int *elements: 51030
2551030 75 10 12
The Copy ConstructorProcess (cont.)
The deepCopy function produces a deep copy of the ap (or arr) Array object, for arr2.
78
int errorCode: 0 int capacity: 4 int *elements: 51030
2551030 75 10 12
arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
The Copy ConstructorProcess (cont.)
The deepCopy function produces a deep copy of the ap (or arr) Array object, for arr2.
ap (or arr)
79
The Copy ConstructorProcess (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 )
template <class DataType>Array<DataType>::Array( const Array<DataType> & ap ){
deepCopy( ap );}
the deepCopy function returns, then the copy constructor returns.
80
The Copy ConstructorProcess (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 )
arr2 now has a deep copy of the arr object (parameter passing has completed)
81
foo( arr );.. // other code...void foo( Array<int> arr2 ){
}
arr int errorCode: 0 int capacity: 4 int *elements: 51030
2551030 75 10 12
arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
The Copy ConstructorProcess (cont.)
82
The Copy ConstructorProcess (cont.)
foo( arr );.. // other code...void foo( Array<int> arr2 ){
.
. // function code
.}
The foo function executes.
83
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.
84
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).
85
Nuts and Bolts ofthe deepCopy Function
1 template <class DataType>2 inline void Array<DataType>::deepCopy( 3 const Array<DataType> & original )4 {5 capacity = original.capacity;6 errorCode = original.errorCode;7 elements = new DataType [capacity];8 for ( int i = 0; i < capacity; i++ )9 elements[ i ] = original.elements[ i ];10 }
from copy constructor: deepCopy( ap );
86
Nuts and Bolts ofthe deepCopy Function
(cont.)1 template <class DataType>2 inline void Array<DataType>::deepCopy( 3 const Array<DataType> & original )4 {5 capacity = original.capacity;6 errorCode = original.errorCode;7 elements = new DataType [capacity];8 for ( int i = 0; i < capacity; i++ )9 elements[ i ] = original.elements[ i ];10 }
capacity of arr2 (in our example)
87
Nuts and Bolts ofthe deepCopy Function
(cont.)1 template <class DataType>2 inline void Array<DataType>::deepCopy( 3 const Array<DataType> & original )4 {5 capacity = original.capacity;6 errorCode = original.errorCode;7 elements = new DataType [capacity];8 for ( int i = 0; i < capacity; i++ )9 elements[ i ] = original.elements[ i ];10 }
capacity of arr (in our example)
88
Nuts and Bolts ofthe deepCopy Function
(cont.)1 template <class DataType>2 inline void Array<DataType>::deepCopy( 3 const Array<DataType> & original )4 {5 capacity = original.capacity;6 errorCode = original.errorCode;7 elements = new DataType [capacity];8 for ( int i = 0; i < capacity; i++ )9 elements[ i ] = original.elements[ i ];10 }
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<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
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
Array ObjectAssignment (cont.)
• But some issues exist in assignment that don’t exist in parameter passing
• Consider…
92
int errorCode: 0 int capacity: 5 int *elements: 51030
2551030
75 10 12arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
Array ObjectAssignment (cont.)
arr
59
Given these initial Array objects, what happens in the deepCopy function for
arr = arr2;
93
int errorCode: 0 int capacity: 5 int *elements: 51030
2551030
75 10 12arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
Array ObjectAssignment (cont.)
arr
59
arr = arr2;The original is arr2 – the deepCopy function is called for arr
94
int errorCode: 0 int capacity: 5 int *elements: 51030
2551030
75 10 12arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
Array ObjectAssignment (cont.)
arr
59
arr = arr2;from deepCopy:capacity = original.capacity;errorCode = original.errorCode;elements = new DataType [capacity];
95
int errorCode: 0 int capacity: 5 int *elements: 51030
2551030
75 10 12arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
Array ObjectAssignment (cont.)
arr
59
arr = arr2;from deepCopy:capacity = original.capacity;errorCode = original.errorCode;elements = new DataType [capacity];
96
int errorCode: 0 int capacity: 4 int *elements: 51030
2551030
75 10 12arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
Array ObjectAssignment (cont.)
arr
59
arr = arr2;from deepCopy:capacity = original.capacity;errorCode = original.errorCode;elements = new DataType [capacity];
97
int errorCode: 0 int capacity: 4 int *elements: 51030
2551030
75 10 12arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
Array ObjectAssignment (cont.)
arr
59
arr = arr2;from deepCopy:capacity = original.capacity;errorCode = original.errorCode;elements = new DataType [capacity];
98
int errorCode: 0 int capacity: 4 int *elements: 51030
2551030
75 10 12arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
Array ObjectAssignment (cont.)
arr
59
arr = arr2;from deepCopy:capacity = original.capacity;errorCode = original.errorCode;elements = new DataType [capacity];
99
int errorCode: 0 int capacity: 4 int *elements: 51030
2551030
75 10 12arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
Array ObjectAssignment (cont.)
arr
59
arr = arr2;from deepCopy:capacity = original.capacity;errorCode = original.errorCode;elements = new DataType [capacity];
100
from deepCopy:capacity = original.capacity;errorCode = original.errorCode;elements = new DataType [capacity];
int errorCode: 0 int capacity: 4 int *elements: 64599
2551030
75 10 12arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
Array ObjectAssignment (cont.)
arr
59
64599
arr = arr2;
101
from deepCopy:capacity = original.capacity;errorCode = original.errorCode;elements = new DataType [capacity];
int errorCode: 0 int capacity: 4 int *elements: 64599
2551030
75 10 12arr2 int errorCode: 0 int capacity: 4 int *elements: 41066
25 75 10 12
41066
Array ObjectAssignment (cont.)
arr
59
64599
MEMORY LEAK!
arr = arr2;
102
Array ObjectAssignment (cont.)
• To solve this problem, we must free the elements array in arr first, before calling deepCopy…
103
Array ObjectAssignment (cont.)
template <class DataType> ……………….. ::operator =( const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );..
}
104
Array ObjectAssignment (cont.)
template <class DataType> ……………….. ::operator =( const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );..
}
But we still need to put in some missing pieces…
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
arr1 = arr2 = arr3;
How MultipleAssignment Works
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 MultipleAssignment Works (cont.)
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 MultipleAssignment Works (cont.)
109
template <class DataType> ……………….. ::operator =( const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );..
}
But how can this function return the object that it is called for?
How MultipleAssignment Works (cont.)
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
template <class DataType> ……………….. ::operator =( const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );
return *this; }
Return *this
112
template <class DataType> ……………….. ::operator =( const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );
return *this; }
Return *this (cont.)
Now we can fill in this part…
113
template <class DataType> Array<DataType> Array<DataType>::operator =(
const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );
return *this; }
Return *this (cont.)
Now we can fill in this part…
114
template <class DataType> Array<DataType> Array<DataType>::operator =(
const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );
return *this; }
Return *this (cont.)
Return type
115
template <class DataType> Array<DataType> Array<DataType>::operator =(
const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );
return *this; }
Return *this (cont.)
The usual class name
116
template <class DataType> Array<DataType> Array<DataType>::operator =(
const Array<DataType> & 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
template <class DataType> Array<DataType> Array<DataType>::operator =(
const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );
return *this; }
Return *this (cont.)
so let’s speed things up a bit and return by reference
118
template <class DataType> Array<DataType> & Array<DataType>::operator =(
const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );
return *this; }
Return *this (cont.)
so let’s speed things up a bit and return by reference
119
template <class DataType> Array<DataType> & Array<DataType>::operator =(
const Array<DataType> & 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
template <class DataType> Array<DataType> & Array<DataType>::operator =(
const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );
return *this; }
Return *this (cont.)
We still have one more piece of the puzzle
121
template <class DataType> Array<DataType> & Array<DataType>::operator =(
const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );
return *this; }
An Object Assignedto 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
template <class DataType> Array<DataType> & Array<DataType>::operator =(
const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );
return *this; }
An Object Assignedto 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
template <class DataType> Array<DataType> & Array<DataType>::operator =(
const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );
return *this; }
An Object Assignedto Itself (cont.)
What happens if an Array object is assigned to itself here?
124
template <class DataType> Array<DataType> & Array<DataType>::operator =(
const Array<DataType> & right ) {
.
.delete [ ] elements;deepCopy( right );
return *this; }
An Object Assignedto 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
template <class DataType> Array<DataType> & Array<DataType>::operator =(
const Array<DataType> & 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 Assignedto Itself (cont.)
126
template <class DataType> Array<DataType> & Array<DataType>::operator =(
const Array<DataType> & right ) {
if ( this == &right )return *this;
delete [ ] elements;deepCopy( right );
return *this; }
An Object Assignedto Itself (cont.)
If they are the same object, return right away.
127
template <class DataType> Array<DataType> & Array<DataType>::operator =(
const Array<DataType> & right ) {
if ( this == &right )return *this;
delete [ ] elements;deepCopy( right );
return *this; }
Complete OverloadedAssignment Operator
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
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