Home C++ Introduction Decisions Loops Input/Output Functions Stack and Heap References Arrays Searching and Sorting Recursion Pointers Character and Strings Structures Classes Inheritance Exceptions Templatess STL Modern C++ Misc Books ----

Classes


Contents

Copy Constructors id="Copy Constructors"> Copy Constructors Sometimes an object is created using another object. In this case a special kind of constructor called a "Copy Constructor" gets called. A default copy constructor is created if we don't define our own. In some cases this can create problems.
File: cc1.cpp
#include <iostream>
using namespace std ;

class Demo
{
  public:
   int x1 ;
   int x2 ;
   int* ptr ;
   Demo()
   {
      cout << "Inside the constructor method." << endl ;
      ptr = new int[10] ;
   }
   ~Demo()
   {
       cout << "Inside the destructor method." << endl ;
       delete[] ptr ;
    }
};

int main()
{
    Demo demo1   ;
    Demo demo2 = demo1    ;

}
$ g++ cc1.cpp  ; ./a.exe
Inside the constructor method.
Inside the destructor method.
Inside the destructor method.
Aborted (core dumped)
We create an object called "demo1" . The constructor allocates some memory that gets assigned to the variable "ptr" . We then use this object to initialize another object "demo2". The default copy constructor gets called and that copies all the data values bit by bit to the variables of "demo1" . Thus the "ptr" of "demo2" will take on the value of "ptr" of "demo1" The object "demo1" gets destructed and releases the memory for "ptr" . The object "demo2" gets destructed and also deletes "ptr" that points to the same address and we get an exception in the program. We should define our own copy constructor to handle this case.
File: cc2.cpp
#include <iostream>
using namespace std ;
class Demo
{
    public:
     int x1 ;
     int x2 ;
     int* ptr ;
     Demo()
     {
         cout << "Inside the construtor method." << endl ;
         ptr = new int[10] ;
         //Initialize with some values
         for( int i1=0; i1<10 ; i1++ )
         {
             ptr[i1] = i1+ 1;
          }
      }
      Demo(const Demo& demoObj )
      {
          cout << "Inside the copy construtor method." << endl ;
          ptr = new int[10] ;
          //Copy the values in the array from demoObj
          for( int i1=0; i1<10 ; i1++ )
           {
               ptr[i1] = demoObj.ptr[i1] ;
            }
      }
      ~Demo()
      {
          cout << "Inside the destrutor method." << endl ;
          delete[] ptr ;
      }
      void print()
      {
          for( int i1=0; i1<10 ; i1++ )
          {
              cout << ptr[i1] << " "  ;
          }
          cout << endl ;
      }
};

int main()
{
    Demo demo1   ;
    Demo demo2 = demo1    ;
    demo2.print() ;
}
$ g++ cc2.cpp  ; ./a.exe
Inside the construtor method.
Inside the copy construtor method.
1 2 3 4 5 6 7 8 9 10
Inside the destrutor method.
Inside the destrutor method
The signature for a copy constructor is of the form:

Demo(const Demo& demoObj )

We function name is the name of the class and the
parameter is a reference. It must be a reference
and not a normal object such as "Demo demoObj".
Otherwise the parameter "demoObj" will try to get
created through the calling method and we have the
copy constructor method getting called  in a loop.

The 2 other cases in which he copy constructor
will get called is when passing the object to the
arguments of a function and when a function returns
an object. The function returning an object doesn't
always end up calling the copy constructor due to
compiler optimization.

File: cc3.cpp
#include <iostream>
using namespace std ;

class Demo
{
    private:
     int z1 ;
    public:
     int x1 ;
     int x2 ;
     string name ;
     int* ptr ;

     Demo(string nameP )
      {
          name = nameP ;
          cout << "Inside the construtor method."<< name << endl ;
          ptr = new int[10] ;
      }
     Demo(const Demo& demoObj )
     {
         cout << "Inside the copy constructor method." <<
         demoObj.name << endl ;
         ptr = new int[10] ;
         name = demoObj.name ;
         //We have access to another object of the same class's
         //private data.
         x1 = demoObj.z1 ;
      }
     ~Demo()
      {
          cout << "Inside the destructor method:" << name << endl ;
          delete[] ptr ;
      }
};

void function1( Demo obj1 )
{
    cout << "Inside function1" << endl ;
}
int main()
{
    {
        Demo demo1("Local1" )    ;
        cout << "Step1:"  <<  endl ;
        function1( demo1 )     ;
        cout << "Step2" << endl ;
    }

}
$ g++ cc3.cpp  ; ./a.exe
Inside the construtor method.Local1
Step1:
Inside the copy constructor method.Local1
Inside function1
Inside the destructor method:Local1
Step2
Inside the destructor method:Local1
The above shows the copy constructor getting called when we call the function "function1".
File: cc4.cpp
#include <iostream>
using namespace std ;

class Demo
{
    private:
     int z1 ;
    public:
     int x1 ;
     int x2 ;
     string name ;
     int* ptr ;

     Demo(string nameP )
      {
          name = nameP ;
          cout << "Inside the construtor method."<< name << endl ;
          ptr = new int[10] ;
      }
     Demo(const Demo& demoObj )
     {
         cout << "Inside the copy constructor method." <<
         demoObj.name << endl ;
         ptr = new int[10] ;
         name = demoObj.name ;
         //We have access to another object of the same class's
         //private data.
         x1 = demoObj.z1 ;
      }
     ~Demo()
      {
          cout << "Inside the destructor method:" << name << endl ;
          delete[] ptr ;
      }
};

Demo function1(  )
{
    Demo obj2("function1") ;
    obj2.x1 = 100 ;
    cout << "Inside function1" << endl ;
    return obj2 ;
}



int main()
{
    {
        Demo demo1("Local1" )    ;
        cout << "Step1:"  <<  endl ;
        Demo  demo2 = function1(  )     ;
        cout << "Step2" << endl ;
    }

}
$ g++ -fno-elide-constructors cc4.cpp  ; ./a.exe
Inside the construtor method.Local1
Step1:
Inside the construtor method.function1
Inside function1
Inside the copy constructor method.function1
Inside the destructor method:function1
Step2
Inside the destructor method:function1
Inside the destructor method:Local1
We use the "-fno-elide-constructors" option to prevent optimization and the copy constructor gets called when the function returns. Let us understand how the optimization is working.


File: cc4a.cpp
#include <iostream>
using namespace std ;

class Demo
{
    private:
     int z1 ;
    public:
     int x1 ;
     int x2 ;
     string name ;
     int* ptr ;

     Demo(string nameP )
      {
          name = nameP ;
          cout << "Inside the construtor method."<< name << endl ;
          ptr = new int[10] ;
      }
     Demo(const Demo& demoObj )
     {
         cout << "Inside the copy constructor method." <<
         demoObj.name << endl ;
         ptr = new int[10] ;
         name = demoObj.name ;
         //We have access to another object of the same class's
         //private data.
         x1 = demoObj.z1 ;
      }
     ~Demo()
      {
          cout << "Inside the destructor method:" << name << endl ;
          delete[] ptr ;
      }
};

Demo function1(  )
{
    Demo obj2("function1") ;
    obj2.x1 = 100 ;
    cout << "Inside function1" << endl ;
    printf( "Address of obj2: %p\n" , &obj2 ) ;
    return obj2 ;
}



int main()
{
    {
        Demo demo1("Local1" )    ;
        cout << "Step1:"  <<  endl ;
        //Demo  demo2 = function1(  )     ;
        function1()     ;
        cout << "Step2" << endl ;
        //printf( "Address of demo2: %p\n" , &demo2 ) ;
    }

}

Inside the construtor method.Local1
Step1:
Inside the construtor method.function1
Inside function1
Address of obj2: 0x7ffffcc00
Inside the destructor method:function1
Step2
Inside the destructor method:Local1

The first constructor get called from when "demo1"
object is created. Then the "function1" is called.
Now the object "obj2" is created. Once the function
ends it goes out of scope and it gets destroyed.
Now let's take the case of the function
return an object.

File: cc4b.cpp
#include <iostream>
using namespace std ;

class Demo
{
    private:
     int z1 ;
    public:
     int x1 ;
     int x2 ;
     string name ;
     int* ptr ;

     Demo(string nameP )
      {
          name = nameP ;
          cout << "Inside the construtor method."<< name << endl ;
          ptr = new int[10] ;
      }
     Demo(const Demo& demoObj )
     {
         cout << "Inside the copy constructor method." <<
         demoObj.name << endl ;
         ptr = new int[10] ;
         name = demoObj.name ;
         //We have access to another object of the same class's
         //private data.
         x1 = demoObj.z1 ;
      }
     ~Demo()
      {
          cout << "Inside the destructor method:" << name << endl ;
          delete[] ptr ;
      }
};

Demo function1(  )
{
    Demo obj2("function1") ;
    obj2.x1 = 100 ;
    cout << "Inside function1" << endl ;
    printf( "Address of obj2: %p\n" , &obj2 ) ;
    return obj2 ;
}



int main()
{
    {
        Demo demo1("Local1" )    ;
        cout << "Step1:"  <<  endl ;
        Demo  demo2 = function1(  )     ;
        //function1()     ;
        cout << "Step2" << endl ;
        printf( "Address of demo2: %p\n" , &demo2 ) ;
    }

}

$ g++ cc4b.cpp  ; ./a.exe
Inside the construtor method.Local1
Step1:
Inside the construtor method.function1
Inside function1
Address of obj2: 0x7ffffcbf0
Step2
Address of demo2: 0x7ffffcbf0
Inside the destructor method:function1
Inside the destructor method:Local1

The compiler does optimization. We reach
"Step 2". The object "obj2" does not
go out of scope. Rather the compiler
puts "demo2" at the place where "obj2" is.
The "obj2" and "obj1" are destroyed when
main exits.
Now let's study the case when compiler optimization is switch off.
$ g++ -fno-elide-constructors cc4b.cpp  ; ./a.exe
Inside the construtor method.Local1
Step1:
Inside the construtor method.function1
Inside function1
Address of obj2: 0x7ffffcb60
Inside the copy constructor method.function1
Inside the destructor method:function1
Step2
Address of demo2: 0x7ffffcbf0
Inside the destructor method:function1
Inside the destructor method:Local1
The function returns and "demo2" is assigned the value of "obj2" via the copy constructor. Then the "obj2" is destroyed.

Exercise

We are going to construct our own string class
called "mystring" .  The below files have been
written out for you: "main.cpp" , "mystring.h" ,
"mystring.cpp" .

Add a constructor without any arguments that sets
the ptr to null .

Add the copy constructor to the mystring class
and run the program.

To compile:

g++ main.cpp mystring.cpp

File: mystring.h
#ifndef MYSTRING_H
#define MYSTRING_H
#include <string.h>
#include <iostream>

using namespace std ;

class mystring
{
    private:
     char* ptr ;
     public:
     mystring( const char* str1 ) ;
     ~mystring() ;
     void print() ;
};

#endif

File: mystring.cpp
#include "mystring.h"

mystring::mystring( const char* str1 )
{
    cout << "Inside the constructor." << endl ;
    if ( str1 == NULL )
      ptr = NULL ;
    else
     {
          int len  = strlen( str1 ) + 1;
          ptr = new char[ len ] ;
          strcpy ( ptr, str1 ) ;
     }
}

mystring::~mystring()
{
    cout << "Inside the destructor." << endl ;
    if ( ptr != NULL )
      delete[] ptr ;
}

void mystring::print()
{
    cout << ptr << endl ;
}


File: main.cpp
#include "mystring.h"

int main()
{
    mystring str1( "Marvin" ) ;
    mystring str2 = str1 ;
    str1.print() ;
    return 0 ;

}