We implicitly copy objects whenever we:
Problem Example 1 (Initialize one object from another )
class intTable {
public:
intTable(int, int);
private:
int len; // number of elements
int *elements; // elements to be filled in dynamically
};
intTable::intTable(int n, int value) {
len = n;
elements = new int[len]; // dynamically allocate elements
for (int i = 0; i < len; i++) elements[i] = value;
}
void someFunction() {
intTable t1(100,0); // make an intTable
intTable t2(t1); // make t2 a copy of t1
t2.put(3, 100); // change element 3
} // destruct both intTable's
Problems with 1.
In this example, we are saying that t2 should be initialized to a copy of t1.
intTable t2(t1); // make t2 a copy of t1
Problem Example 2 (a call-by-value)
class intTable {
public:
intTable(int, int);
intTable operator = (intTable &);
private:
int len; // number of elements
int *elements; // elements to be filled in dynamically
};
intTable::intTable(int n, int value) {
len = n;
elements = new int[len]; // dynamically allocate elements
for (int i = 0; i < len; i++) elements[i] = value;
}
intTable::operator = (intTable & A) {
len = A.len;
elements = A.elements;
return *this;
}
void someFunction() {
intTable t1(100,0) ; // make an intTable
intTable t2 = t1; // make t2 a copy of t1
t2.put(3, 100); // change element 3
} // destruct both intTable's
Problems with 2.
In this example, we are saying that t2 should be initialized to a copy of t1.
t2 = t1; // make t2 a copy of t1
Problem Example 3 (return-by-value)
class intTable {
public:
intTable(int, int);
intTable Add(intTable &);
private:
int len; // number of elements
int *elements; // elements to be filled in dynamically
};
intTable::intTable(int n, int value) {
len = n;
elements = new int[len]; // dynamically allocate elements
for (int i = 0; i < len; i++) elements[i] = value;
}
intTable intTable::Add(intTable & A) {
intTable Temp(A.len,0);
for(int i = 0; i < A.len; i++) Temp.elements[i] = elements[i] + A.elements[i];
return Temp;
}
void someFunction() {
intTable t1(100,0), t2(100,2); // make an intTable
intTable t3(t1.ADD(t2)); // add t1 and t2 and create t3
t2.put(3, 100); // change element 3
} // destruct both intTable's
Problems with 3.
In this example, we are saying that t3 should be initialized to a copy of t1 added to t2.
intTable t3(t1.ADD(t2)); // t1 added to t2, initialize t3
Discussion
In general, the copy constructor is declared as:
type(const type& other);
Example:
intTable(const intTable& other)
Example:
intTable::intTable(const intTable &other) {
len = other.len; // allocate space
elements = new int[len];
for (int i = 0; i < len; i++) // initialize space
elements[i] = other.elements[i];
}
More Discussion
Sometimes executing a copy constructor is an expensive operation (e.g., copying a linked list).
Unfortunately, that means code like the following can be very inefficient:
List x;
... // add lots of elements to x
List y(x); // copy all of x to initialize y
Array Example*
1 // Simple class Array (for integers)
2 #include <iostream.h>
3
4 class Array {
5 friend ostream &operator<<( ostream &, const Array & );
6 friend istream &operator>>( istream &, Array & );
7 public:
8 Array( int = 10 ); // default constructor
9 Array( const Array & ); // copy constructor
10 ~Array(); // destructor
11 int getSize() const; // return size
12 const Array &operator=( const Array & ); // assign arrays
13 bool operator==( const Array & ) const; // compare equal
14 // Determine if two arrays are not equal and
15 // return true, otherwise return false (uses operator==).
16 bool operator!=( const Array &right ) const { return ! ( *this == right );}
17 int &operator[]( int ); // subscript operator
18 const int &operator[]( int ) const; // subscript operator
19 static int getArrayCount(); // Return count of
20 // arrays instantiated.
21 private:
22 int size; // size of the array
23 int *ptr; // pointer to first element of array
24 static int arrayCount; // # of Arrays instantiated
25 };
26
27 // Member function definitions for class Array
28 #include <iostream.h>
29 #include <iomanip.h>
30 #include <stdlib.h>
31 #include <assert.h>
32 #include "array1.h"
33
34 // Initialize static data member at file scope
35 int Array::arrayCount = 0; // no objects yet
36
37 // Default constructor for class Array (default size 10)
38 Array::Array( int arraySize ) {
39 size = ( arraySize > 0 ? arraySize : 10 );
40 ptr = new int[ size ]; // create space for array
41 assert( ptr != 0 ); // terminate if memory not allocated
42 ++arrayCount; // count one more object
43
44 for ( int i = 0; i < size; i++ )
45 ptr[ i ] = 0; // initialize array
46 }
47
48 // Copy constructor for class Array
49 // must receive a reference to prevent infinite recursion
50 Array::Array( const Array &init ) : size( init.size ) {
51 ptr = new int[ size ]; // create space for array
52 assert( ptr != 0 ); // terminate if memory not allocated
53 ++arrayCount; // count one more object
54
55 for ( int i = 0; i < size; i++ )
56 ptr[ i ] = init.ptr[ i ]; // copy init into object
57 }
58
59 // Destructor for class Array
60 Array::~Array() {
61 delete [] ptr; // reclaim space for array
62 --arrayCount; // one fewer objects
63 }
64
65 // Get the size of the array
66 int Array::getSize() const { return size; }
67
68 // Overloaded assignment operator
69 // const return avoids: ( a1 = a2 ) = a3
70 const Array &Array::operator=( const Array &right )
71 {
72 if ( &right != this ) { // check for self-assignment
73
74 // for arrays of different sizes, deallocate original
75 // left side array, then allocate new left side array.
76 if ( size != right.size ) {
77 delete [] ptr; // reclaim space
78 size = right.size; // resize this object
79 ptr = new int[ size ]; // create space for array copy
80 assert( ptr != 0 ); // terminate if not allocated
81 }
82
83 for ( int i = 0; i < size; i++ )
84 ptr[ i ] = right.ptr[ i ]; // copy array into object
85 }
86
87 return *this; // enables x = y = z;
88 }
89
90 // Determine if two arrays are equal and
91 // return true, otherwise return false.
92 bool Array::operator==( const Array &right ) const {
93 if ( size != right.size )
94 return false; // arrays of different sizes
95
96 for ( int i = 0; i < size; i++ )
97 if ( ptr[ i ] != right.ptr[ i ] )
98 return false; // arrays are not equal
99
100 return true; // arrays are equal
101 }
102
103 // Overloaded subscript operator for non-const Arrays
104 // reference return creates an lvalue
105 int &Array::operator[]( int subscript ) {
106 // check for subscript out of range error
107 assert( 0 <= subscript && subscript < size );
108
109 return ptr[ subscript ]; // reference return
110 }
111
112 // Overloaded subscript operator for const Arrays
113 // const reference return creates an rvalue
114 const int &Array::operator[]( int subscript ) const {
115 // check for subscript out of range error
116 assert( 0 <= subscript && subscript < size );
117
118 return ptr[ subscript ]; // const reference return
119 }
120
121 // Return the number of Array objects instantiated
122 // static functions cannot be const
123 int Array::getArrayCount() { return arrayCount; }
124
125 // Overloaded input operator for class Array;
126 // inputs values for entire array.
127 istream &operator>>( istream &input, Array &a ) {
128 for ( int i = 0; i < a.size; i++ )
129 input >> a.ptr[ i ];
130
131 return input; // enables cin >> x >> y;
132 }
133
134 // Overloaded output operator for class Array
135 ostream &operator<<( ostream &output, const Array &a ) {
136 int i;
137
138 for ( i = 0; i < a.size; i++ ) {
139 output << setw( 12 ) << a.ptr[ i ];
140
141 if ( ( i + 1 ) % 4 == 0 ) // 4 numbers per row of output
142 output << endl;
143 }
144 if ( i % 4 != 0 )
145 output << endl;
146
147 return output; // enables cout << x << y;
148 }