Handout 21a

Copy Constructors


Introduction

We implicitly copy objects whenever we:

  1. Initialize one object from another (by default, the new object is initalized by copying the existing objects).A variable X is declared to be a copy of another variable Y. The copy constructor is invoked on X and passed Y.
  2. Do a call-by-value (by default, the parameter is initalized by copying the passed-object). A variable X in a function is declared as an object, not a reference. The copy constructor is invoked on X and passed whatever value V was passed to the function.
  3. Do a return-by-value (by default, the temporary object holding the return-value is initalized by copying the returned-value). A function returns an object Y. The system creates a temporary object X and invokes the copy constructor on X, passing it Y.

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  }


*array example from Deitel&Deitel 2E and portions from http://www-ee.eng.hawaii.edu/~alex/Courses/EE467/Spring98/Lectures/02-11/Slides/private.htm.