Navigation



9.11 — The copy constructor and overloading the assignment operator

Although using the assignment operator is fairly straightforward, correctly implementing an overloaded assignment operator can be a little more tricky than you might anticipate. There are two primary reasons for this. First, there are some cases where the assignment operator isn’t called when you might expect it to be. Second, there are some issues in dealing with dynamically allocated memory (which we will cover in the next lesson).

The assignment operator is used to copy the values from one object to another already existing object. The key words here are “already existing”. Consider the following example:

Cents cMark(5); // calls Cents constructor
Cents cNancy; // calls Cents default constructor
cNancy = cMark; // calls Cents assignment operator

In this case, cNancy has already been created by the time the assignment is executed. Consequently, the Cents assignment operator is called. The assignment operator must be overloaded as a member function.

What happens if the object being copied into does not already exist? To understand what happens in that case, we need to talk about the copy constructor.

The copy constructor

Consider the following example:

Cents cMark(5); // calls Cents constructor
Cents cNancy = cMark; // calls Cents copy constructor!

Because the second statement uses an equals symbol in it, you might expect that it calls the assignment operator. However, it doesn’t! It actually calls a special type of constructor called a copy constructor. A copy constructor is a special constructor that initializes a new object from an existing object.

The purpose of the copy constructor and the assignment operator are almost equivalent — both copy one object to another. However, the assignment operator copies to existing objects, and the copy constructor copies to newly created objects.

The difference between the copy constructor and the assignment operator causes a lot of confusion for new programmers, but it’s really not all that difficult. Summarizing:

  • If a new object has to be created before the copying can occur, the copy constructor is used.
  • If a new object does not have to be created before the copying can occur, the assignment operator is used.

There are three general cases where the copy constructor is called instead of the assignment operator:

  1. When instantiating one object and initializing it with values from another object (as in the example above).
  2. When passing an object by value.
  3. When an object is returned from a function by value.

In each of these cases, a new variable needs to be created before the values can be copied — hence the use of the copy constructor.

Because the copy constructor and assignment operator essentially do the same job (they are just called in different cases), the code needed to implement them is almost identical.

An overloaded assignment operator and copy constructor example

Now that you understand the difference between the copy constructor and assignment operator, let’s see how they are implemented. For simple classes such as our Cents class, it is very straightforward.

Here is a simplified version of our Cents class:

class Cents
{
private:
    int m_nCents;
public:
    Cents(int nCents=0)
    {
        m_nCents = nCents;
    }
};

First, let’s add the copy constructor. Thinking about this logically, because it is a constructor, it needs to be named Cents. Because it needs to copy an existing object, it needs to take a Cents object as a parameter. And finally, because it is a constructor, it doesn’t have a return type. Putting all of these things together, here is our Cents class with a copy constructor.

class Cents
{
private:
    int m_nCents;
public:
    Cents(int nCents=0)
    {
        m_nCents = nCents;
    }

    // Copy constructor
    Cents(const Cents &cSource)
    {
        m_nCents = cSource.m_nCents;
    }
};

A copy constructor looks just like a normal constructor that takes a parameter of the class type. However, there are two things which are worth explicitly mentioning. First, because our copy constructor is a member of Cents, and our parameter is a Cents, we can directly access the internal private data of our parameter. Second, the parameter MUST be passed by reference, and not by value. Can you figure out why?

The answer lies above in the list that shows the cases where a copy constructor is called. A copy constructor is called when a parameter is passed by value. If we pass our cSource parameter by value, it would need to call the copy constructor to do so. But calling the copy constructor again would mean the parameter is passed by value again, requiring another call to the copy constructor. This would result in an infinite recursion (well, until the stack memory ran out and the the program crashed). Fortunately, modern C++ compilers will produce an error if you try to do this:

C:\\Test.cpp(431) : error C2652: 'Cents' : illegal copy constructor: first parameter must not be a 'Cents'

The first parameter in this case must be a reference to a Cents!

Now let’s overload the assignment operator. Following the same logic, the prototype and implementation are fairly straightforward:

class Cents
{
private:
    int m_nCents;
public:
    Cents(int nCents=0)
    {
        m_nCents = nCents;
    }

    // Copy constructor
    Cents(const Cents &cSource)
    {
        m_nCents = cSource.m_nCents;
    }

    Cents& operator= (const Cents &cSource);

};

Cents& Cents::operator= (const Cents &cSource)
{
    // do the copy
    m_nCents = cSource.m_nCents;

    // return the existing object
    return *this;
}

A couple of things to note here: First, the line that does the copying is exactly identical to the one in the copy constructor. This is typical. In order to reduce duplicate code, the portion of the code that does the actual copying could be moved to a private member function that the copy constructor and overloaded assignment operator both call. Second, we’re returning *this so we can chain multiple assigments together:

cMark = cNancy = cFred = cJoe; // assign cJoe to everyone

If you need a refresher on chaining, we cover that in the section on overloading the I/O operators.

Finally, note that it is possible in C++ to do a self-assignment:

cMark = cMark; // valid assignment

In these cases, the assignment operator doesn’t need to do anything (and if the class uses dynamic memory, it can be dangerous if it does). It is a good idea to do a check for self-assignment at the top of an overloaded assignment operator. Here is an example of how to do that:

Cents& Cents::operator= (const Cents &cSource)
{
    // check for self-assignment by comparing the address of the
    // implicit object and the parameter
    if (this == &cSource)
        return *this;

    // do the copy
    m_nCents = cSource.m_nCents;

    // return the existing object
    return *this;
}

Note that there is no need to check for self-assignment in a copy-constructor. This is because the copy constructor is only called when new objects are being constructed, and there is no way to assign a newly created object to itself in a way that calls to copy constructor.

Default memberwise copying

Just like other constructors, C++ will provide a default copy constructor if you do not provide one yourself. However, unlike other operators, C++ will provide a default assignment operator if you do not provide one yourself!

Because C++ does not know much about your class, the default copy constructor and default assignment operators it provides are very simple. They use a copying method known as a memberwise copy (also known as a shallow copy). We will talk more about shallow and deep copying in the next lesson.

9.12 — Shallow vs. deep copying
Index
9.10 — Overloading typecasts

38 comments to 9.11 — The copy constructor and overloading the assignment operator

  • jinendra

    Why cant we overload assignment operator using a friend function.
    The operators (), [], -> and = can be overloaded only as class members; all other overloaded operators can (also) be implemented as friends

  • [...] 2007 Prev/Next Posts « 9.11 — The copy constructor and overloading the assignment operator | Home | LearnCpp.com tutorials: Now with syntax highlighting! » Friday, November 9th, [...]

  • PRABHAKAR

    dear alex, my query may look funny. but i dare to put up. in the code on lines 26 // return the existing object
    27 return *this; but u have not declared any
    object which can be termed as ‘existing object’.
    OR u mean to the class as object? please make me aware of my shortcoming in mt understanding. thanks prabhakar

    • Prabhakar, all non-static member functions must have an instance of the class that they are currently working with. For example, when you do this:

      Cents cCents(15);
      

      This will call the Cents constructor, with the cCents object as the implicit object. Inside the constructor, you can refer to the cCents object directly via the this pointer. When I say “existing object”, I mean the implicit object the function is currently working with.

  • Tom

    Hello again Alex -

    I am having trouble grasping when and where to use *this. I will go back and re-read that chapter, but it occurs to me that it might help me (and others) if you had a symbolic diagram of the stack so I could clearly see what’s going on when *this is returned by a function.

    Thanks – this website is a great intro to C++.

    • “return *this” simply returns the object that the member function is working with to the caller.

      Thus, if you called:

      foo.someMemberFunction();

      and someMemberFunction() returned *this, someMemberFunction() would return foo.

      The hardest thing for most people seems to be just understanding what the this pointer is doing in the first place. The actual return mechanics are the same as with any other return value.

      • mslade

        This confused me for a bit, too, so I’ll offer my understanding that it might help someone else.

        Returning *this will dereference the “this” pointer, and instead return the actual object itself. What I got tripped up on is why it wasn’t thus creating a copy of this and returning that. The answer is because this function returns a Cents& (reference). As a result, the object is not copied and instead the capturing value gets the proper object.

  • [...] 2007 Prev/Next Posts « 9.9 — Overloading the parenthesis operator | Home | 9.11 — The copy constructor and overloading the assignment operator » Tuesday, October 30th, 2007 at 12:54 [...]

  • saravanan

    class sar
    {
    public:
    char ini;
    char* name;
    int age;
    sar(int i,char* n):age(i),name(n)
    {
    }
    sar(sar& s)
    {
    *this = s;
    }
    sar& operator=(sar& s)
    {
    return *this;
    }

    };

    int main()
    {
    sar *s = new sar(3,"dd");
    sar s1(*s);
    delete [] s;
    s = NULL;
    cout<<s1.nmae;//prints successfully "dd"!! ..which i thought throw exception.
    }

    alex sir, in above snippet the *s is created in heap, and s1 is created in stack,while creating s1 , by the copy constructor , i am copying the valeus of s object to s1.

    so in debugger in s memory location name is there with adress “abxxx”,
    and also s1′s name’s address also pointed to the same.(as by the copy constructed ).

    then i deleted s , so the name is heap disappered in the address inside the s object layout in heap memory,–> the same address get displayed in debugger for s1′s name , but value is there in same address.

    and i think while deleting s address, name got exhausted and s1′s name also deleted, but that not happened and i successfully displayed the s1′s name!!!!

    please clarify

    • When I copied your program into Visual Studio and ran it, it crashed. This is what I would expect. When you do a *this = s, you’re doing a shallow copy of the members of the class, which means s1.name = s.name. When you delete s, s1.name will point to garbage, and when you try and access it, you’re accessing unallocated memory.

      As far as I know, there’s nothing in the C++ spec that says what compilers have to do with the memory that’s deleted — so I’m guessing in your case the compiler deallocated the memory but didn’t clear it. When you printed s1.name, you actually accessed deallocated memory, but for whatever reason with your combination of compiler/OS it didn’t flag this as a violation and halt the process. This can be a cause of intermittent crashes.

      • Joris

        No the reason it crashes is not that the string is deleted, s1.name will not be pointing to garbage, it will continue to point to the correct string.

        The code is likely to crash, but that’s simply because:
        - You should use delete instead of delete[]; just a typo I assume
        - You are deferencing a uninitialised variable s1.name.
        It wasn’t copied as your copy constructor only calls the assignment operator, and the assignment operator simply returns without any copying, this s1.name was never initialised.

        Assuming this code instead:

        class sar
        {
        public:
          char ini;
          char *name;
          int age;
        
          sar(int i,char* n) : age(i), name(n)
          {
          }
        
          sar(sar& s)
          {
            *this = s;
          }
        
          sar& operator=(sar& s)
          {
            name=s.name;
            return *this;
          }
        };
        
        int main()
        {
          sar *s = new sar(3,"dd");
          sar s1(*s);
          delete s;
          s = NULL;
          cout << s1.nmae;//prints successfully "dd"!! ..which i thought throw exception.
        }
        

        This is guarenteed to print “dd”.
        You seem to be assuming the string “dd” would be deleted. It won’t – the program would probably crash if it tried, as that’s just wrong (it has never been created by ‘new’ so it must not be deleted)

        “dd” is an implied pointer to an array of three characters str={‘d’,'d’,”};
        First it’s assigned to s.name. Then (in my version of the code, not in your original version) it’s copied over to s1.name. Then you delete s, but this will not change this pointer, then you print it; no problem at all.

        The fact that your original code returned “dd” was, luck/bad luck, depending on the compiler or so; there’s no reasonable reason why it would have print “dd” at all;

        This shows an important point – even if code is broken it can appear to work as you intended. The effect that you saw that unchanged code suddenly stopped working, is a sign you are most likely reading uninitialised variables.

        Hope that makes it a bit more clear.

  • saravanan

    thank you Alex,
    but today i ran the same code and i got the exception as you said,
    i am queered , this code ran successfully at that time.
    what may be the reason for this behavior?.

  • Hi Alex, Joris and Saravanan,

    This site is really helpful!

    I’ve tried both versions and neither one work due to a typo of nmae. I corrected the typo and both ran without crash. Here is the details.

    1. OS: Windows Vista Home.

    2. Compiler: The compiler I used is Microsoft Visual C++ 2008 Express Edition. It’s a free download trial version from Microsoft and will expire in 14 days. Any one who knows of a good free compiler and editor please recommend to me. Thank you in advance!

    3. Outputs: I’ve tried many times and the outputs appear to be stable. As Alex pointed out the output of the original version is garbage. Both statements “delete [] s;” and “delete s;” seem to produce the same garbage output from time to time. Joris’ version does display “dd” as expected because of the name=s.name; statement explicitly assigns “dd” to name.

    4. Modifications: I modified the last statement from Joris’ version as follow

    cout << "s1.ini  = " << s1.ini  << endl;
    cout << "s1.name = " << s1.name << endl;//prints successfully "dd"!! ..which i thought throw exception.
    cout << "s1.age  = " << s1.age  << endl;
    

    The content displayed for s1.ini is always the symbol like a rotated “8″
    The s1.name is always “dd” of course.
    The s1.age is a random 7 digit integer eg 3141816 or 1568088.

    Can anybody explain the results and the following questions?
    The “sar s1(*s);” statement explicitly calls the copy constructor. Why is the assignment operator instead of the copy constructor actually executed? When will the copy constructor get called and when assignment operator? Can we force a specific function at our choice to run?

    Thank you very much for your generous contributions!
    Lee

  • I want to take 1 pointer in my class which will point to some integer at run time. After that i will create one object and after that i will try to create one another object from the previous one with copy constructor. I was trying the following code but some how unable to access that or confuse in writing that code.
    class Myclass
    {
    int * ptr ;
    int i ;
    Myclass()
    {
    ptr =new int ;
    *ptr =20;
    i=30;
    }
    Myclass (const Myclass &a)
    {
    this->i= a.i ;
    //But confuse in how to initialize the value which will be point by the pointer i am trying this way
    *p =*(a.p)//Please tell whether this is correct or not
    }
    };

    void main()
    {
    Myclass M;
    Myclass M1 =M ;
    }

  • Jason

    Hi, Alex,

    Actually, many compilers (e.g g++) avoid calling copy constructor when returning an object from function by so called NRV (named-return-value) optimization.

    class A {};
    A f()
    {
      A a;
      return a;
    }
    
    int main()
    {
      A aa = f();   // the normal constructor is called, no copy constructor
    }
    

    However MS visual 2010 call the copy constructor when building debug version program, but won’t call it when building release version. This is a disater for developper, who may see two results during development.

  • Sanjeev

    Hi,
    For the class A can I have copy constructor like A(const A*) instead of A(const A&). What will be the difference. Can anybody please explain?

    • HYA

      Hi Sanjeev
      In that case you have to write any object intialization like this A a, aa1 = &a; … which is a bit confusing …but you can implement both copy contructors as
      class Aclass
      {
      int i ;
      public:
      Aclass(){ i=37; }

      Aclass (const Aclass* a){
      this->i= a->i ;
      cout<i= a.i ;
      cout<<"Copy contructor with reference called";
      }
      int Geti() {return i;}
      };

      int _tmain(int argc, _TCHAR* argv[])
      {
      Aclass aa;
      Aclass aa1 = &aa ;
      Aclass aa2 = aa;

      cout << aa1.Geti();
      }

    • HYA

      class Aclass
      {
      int i ;
      public:
      Aclass(){ i=37; }

      Aclass (const Aclass* a){
      this->i= a->i ;
      cout<i= a.i ;
      cout<<"Copy contructor with reference called";
      }
      int Geti() {return i;}
      };

  • subbaiahtp

    class A
    {

    private:

    char *fname;
    int age;

    };

    class B
    {
    private:
    A aobj;
    char *lname;
    int marks;
    };

    Please let me know the B’s copy constructor implementation?

    • subrat

      class A {
      private:
      char *fname;
      int age;
      public:
      /*A() {
      std::cout<<"zero ctr A\n"<fname = new char[strlen(fname)+1];
      this->age=age;
      std::cout<<"1 cstr—a…."<<this<<std::endl;
      }
      A(const A& val){
      age=val.age;
      fname = new char[strlen(val.fname)+1];
      strcpy(fname, val.fname);
      std::cout<<"Cpy Cstr—A…."<<this<<std::endl;
      }

      ~A(){
      delete[] fname;
      std::cout<<"Destr—A…"<<this<lname=new char[strlen(lname)+1];
      this->marks= marks;
      std::cout<<"constrr B……."<<this<<std::endl;
      }
      B(const B& val):aobj(val.aobj) {
      lname=new char[strlen(val.lname)+1];
      strcpy(lname, val.lname);
      marks= val.marks;
      std::cout<<"copy Cstr—B::::"<<this<<std::endl;

      }

      ~B(){
      delete[] lname;
      std::cout<<"Destr—B:::::"<<this<<std::endl;
      }
      };

  • subbaiahtp

    Thanks subrat :-)

  • saurabh

    Here is answer to why dont we use pass by pointer in copy constructor so see following scenario
    class ABC
    {
    public:
    ABC(){} /*Simple Constructor*/
    ABC(ABC * b) { }/*Copy Constructor*/
    };
    int main(int argc, char* argv[]){
    ABC * d1 = NULL;
    ABC d2 = d1; /*Calling Copy constructor*/
    return 0;
    }
    See above example , if we take pointer type object in copy constructor
    two flaws are here
    1) We can assign NULL value to simple object of that class
    2) see this line ABC d2 = d1;
    here d2 is ABC type while d1 is ABC* type means as per rule we are violating basic rules by allowing to assign simple type with pointer type.
    If there is any pointer type member variable in side ABC class means DEEP Copy scenario like int * m_iVal; then it will crash out during calling copy constructor by passing NULL object, so stop such mistake at design time we do use const reference object of same class type in copy constructor as a parameter.Hoping this will clear you.

  • rohitnitp

    how it come while defining copy constructor you used object of the same class to access private data member which belongs to that object only..since now this object as it is already created how can we access private data directly to anywhere else even if it copy constructor of the same class..it will violate the idea of data encapsulation and data hiding..i tried to access m_nCents via GetLength() at that point but compiler has given me error..why is it so,please explain..

  • dmanniteaux

    Hi Alex,

    Can you explain further why in the code:

    Cents& operator= (const Cents &cSource);

    We require operator= to be passed by reference?

    I am assuming it’s similar to the explanation of ” .. Second, the parameter MUST be passed by reference, and not by value. Can you figure out why?” for the copy constructor part but it would be helpful if you could go into the same detail with this case as well so I can understand :)

    Thanks!!

  • wgrant

    Hey Alex,

    I’m trying to work out how the chaining works in

    cMark = cNancy = cFred = cJoe

    I don’t see how cJoe is ever correctly returned as the object by *this to enable assigning cJoe to any of the other objects.

    Could you please do a step-by-step of how this is done?
    Thanks in advance,
    Will

    • wgrant

      Haha nevermind. I think I got it.

      cMark = cNancy = cFred = cJoe
      cMark = cNancy = (cFred = cJoe) //cFred now holds cJoe
      cMark = (cNancy = cFred) //cNancy now holds cJoe via cFred
      (cMark = cNancy) // cMark now holds cJoe via cNancy via cFred

      I was held up by the *this pointer returning the object on the left side of the assignment operator. I originally thought we had to return cJoe in all cases to assign cJoe to everyone. But returning the operator that was acted upon is correct since it now holds the desired values — which will be propagated down to every object.

      Will

  • HYA

    Hi Alex
    Can I ask one question, as you wrote
    Cents cMark(5); // calls Cents constructor
    Cents cNancy; // calls Cents default constructor
    which constructor is called when I write as
    Cents cJacob();
    or what happen in this case …

  • HYA

    Hi Alex
    I have one more question, what is used, like Copy constructor or Assignment Operator in the case
    Cents coin = Cent(10);
    like to know about this statement.

  • UncaAlby

    Alex, you mentioned that the Copy Constructor and the Assignment Operator Overload could both call the same function to perform the actual copy from the new object to the object copied to. Obviously that is a good idea from the perspective of not duplicating code.

    What are your thoughts on instead using the operator=() function in the copy constructor?

    E.g.:

    // assignment operator overload
    Type &operator=(const Type & newObj) {
    if (this != &newObj) {
    this->member = newObj.member;
    // etc. etc. etc.
    }
    return *this;
    }

    // copy constructor
    Type(const Type & newObj) {
    this->operator=(newObj);
    }

    I’ve tried it, and it seems to work, but I don’t if there might be something about it to bite me on the backside.

    Cheers!

    UncaAlby

    • UncaAlby

      Nevermind — I did some more research, and consensus appears to be that, due to certain members getting default values anyway, doing it this way is a bad idea.

      So, just have the copy constructor and the operator= both call the same copy private method.

      • UncaAlby

        adding to that — since the operator=() only is used when the destination object already exists, you might need to be careful with a deep copy that needs to allocate memory for certain members.

        That is, if you have members that allocate memory in the constructors, you’ll have to free that memory in the operator=() method before you perform the deep copy that will re-allocate new memory for the copy. Otherwise, you can get a memory leak.

  • tunvir rahman tusher

    why my copy constructor is not calling when i am assigning t=t1+t2????

    ///////////////////////
    #include
    using namespace std;
    class test
    {
    int x,y;
    public:
    test(){x=0;y=0;}
    test(int i,int j){x=i;y=j;}
    test(test &ob){cout<x+ob.x;
    t.y=this->y+ob.y;
    return t;
    }
    int main()
    {
    test t,t1(1,2),t2(3,4);
    int x,y;
    t=t1+t2;/////i can’t understand why test(test &ob) is not calling??
    t.get(x,y);
    t=t1;
    //test tx=t1;/////but this call copy constructor???
    t.get(x,y);
    cout<<y;
    getchar();
    }
    ///////////////////////

    thanks in advance

  • 1krishna23

    Hello,
    Class A
    {
    Private:
    Char name [10];
    Public:
    A (const A &obj);
    };
    For this copy constructor is
    A::A (const A &obj)
    {
    Strcpy (name, obj.name);
    }

    Class B {
    Private:
    Char name 1[10], name2, name3, name4…and so on …name1000;
    Public:
    B (const B &obj);
    }
    How can i Write a copy constructor for the class B with 1000 name as char variable,instead of declaring all variables in Class A

  • aaishaa

    Hello,

    As you mentioned copy constructor is called when destination is NOT and already created object. However, in case of return value, If i save the function return value in an “already existing” object, i still calls copy constructor. Can you please clarify this?

    #include
    using namespace std;
    class Cents
    {
    public:
    int m_nCents;
    Cents(int nCents=0)
    {
    m_nCents = nCents;
    }

    Cents(const Cents &cSource)
    {
    m_nCents = cSource.m_nCents;
    cout << "in copy Constructor";
    }

    Cents returnObjectByVal()
    {
    Cents temp;
    temp.m_nCents=100;
    return temp;
    }
    };

    int main()
    {

    Cents obj1;
    Cents obj2;
    obj2= obj1.returnObjectByVal(); // COPY CONSTRUCTOR IS CALLED HERE, HOWEVER, IT SHOULDN'T AS OBJ2 IS aLREADY CREATED
    }

  • [...] the previous lesson on The copy constructor and overloading the assignment operator, you learned about the differences and similarities of the copy constructor and the assignment [...]

You must be logged in to post a comment.