Summary of copy constructors and overloaded assignment operators in C++

  • 2020-04-02 02:48:53
  • OfStack

preface

This article summarizes the copy constructors and overloaded assignment operators in C++, including the following:

1. Definition of copy constructor and overloaded assignment operator;
2. Call timing of copy constructor and overloaded assignment operator;
3. Key points of the implementation of copy constructor and overloaded assignment operator;
4. Copy some details of the constructor.

Copy the definition of the constructor and overloaded assignment operator

As we all know, in C++, a class must contain a constructor, destructor, copy constructor, and overloaded assignment. The compiler will generate four of these functions for you even if you don't define them. For example, the following classes:


class CTest
{
public:
     CTest();
     ~CTest();
 
     CTest(const CTest &);
     void operator=(const CTest &);
};

Today's focus is not on constructors and destructors, but on copy constructors and overloaded assignments. The copy constructor prototype of the class is as follows:

class_name(const class_name &src);

In general, if we don't write a copy constructor, the compiler automatically creates a copy constructor (also called an implicit copy constructor) for each class. Conversely, if we write a copy constructor (an explicit copy constructor), the compiler does not create it.

The prototype of the overloaded assignment operator for the class is as follows:


void operator=(const class_name &);

An overloaded assignment operator is a special assignment operator, usually used to specify an existing object to another of the same type. It is a special member function, if we do not define the member function, then the compiler will automatically produce this member function. The code generated by the compiler is the action of copying an object as a single member.

The definition of the copy constructor and the overloaded assignment operator is summarized, just to let us know about them without really going into them. Next, carefully summarize their timing. I've never really understood the timing of their calls, so it must be a good summary.

The timing of the invocation of the copy constructor and the overloaded assignment operator

Calls to the copy constructor and to the overloaded assignment operator always occur inadvertently, and they are executed without us explicitly calling them. Pay more attention to this implicit invocation, which is usually a trap. Now I'm going to do it with a real example; Here are some examples:


#include <iostream>
using namespace std;
 
class CTest
{
public:
     CTest(){}
     ~CTest(){}
 
     CTest(const CTest &test)
     {
          cout<<"copy constructor."<<endl;
     }
 
     void operator=(const CTest &test)
     {
          cout<<"operator="<<endl;
     }
 
     void Test(CTest test)
     {}
 
     CTest Test2()
     {
          CTest a;
          return a;
     }
 
     void Test3(CTest &test)
     {}
 
     CTest &Test4()
     {
          CTest *pA = new CTest;
          return *pA;
     }
};
 
int main()
{
     CTest obj;
 
     CTest obj1(obj); //Call the copy constructor
 
     obj1 = obj; //Call the overloaded assignment operator
 
     /* During parameter passing, the copy constructor is called once
     * obj1 When pushed, the copy constructor is called to create a temporary object with the same scope as the local variables in the function
     */
     obj.Test(obj1);
 
     /* When the function returns a value, the copy constructor is called. Assign the return value to obj2 , the overloaded assignment operator is called
     * When a function returns a value, it also constructs a temporary object. The copy constructor is called to copy the return value to the temporary object
     */
     CTest obj2 ;
     obj2 = obj.Test2();
 
     obj2.Test3(obj); //The argument is a reference and the copy constructor
is not called  
     CTest obj3;
     obj2.Test4(); //The return value is a reference and the copy constructor
is not called  
     return 0;
}

Comments have been added to the code, so I won't go into details here. Again, if an object is declared and assigned another existing object to it, the copy constructor is called. If an object already exists, and another existing object is assigned to it, the overloaded assignment operator is called. That's a good rule to keep in mind.

Implementation points for copy constructors and overloaded assignment operators

In general, the default copy constructor and overloaded assignment operator generated by the compiler are sufficient. However, in some special cases, we need to manually implement our own copy constructor.

As we all know, the default copy constructor and assignment operator both do "shallow copy" and simply copy the field, so if the object contains dynamically allocated memory, we need to rewrite the copy constructor or override the assignment operator to implement "deep copy" to ensure data integrity and security. This is often referred to as the deep and shallow copy problem. Here is a simple example:


#include <iostream>
using namespace std;
 
const int MAXSIZE = 260;
 
class CTest
{
public:
     CTest(wchar_t *pInitValue)
     {
          // Here, I malloc the memory
          pValue = new wchar_t[MAXSIZE];
          memset(pValue, 0, sizeof(wchar_t) * MAXSIZE);
          wcscpy_s(pValue, MAXSIZE, pInitValue);
     }
 
     ~CTest()
     {
          if (pValue)
          {
               delete[] pValue; //Finalseabiscuit points out, thanks. 2014.7.24 < br / >                pValue = NULL;
          }
     }
 
     CTest(const CTest &test)
     {
          // Malloc the new memory for the pValue
          pValue = new wchar_t[MAXSIZE];
          memset(pValue, 0, sizeof(wchar_t) * MAXSIZE);
          wcscpy_s(pValue, MAXSIZE, test.pValue);
     }
 
     CTest& operator=(const CTest &test)
     {
          // This is very important, please remember
          if (this == &test)
          {
               return *this;
          }
 
          // Please delete the memory, this maybe cause the memory leak
          if (pValue)
          {
               delete[] pValue; //Fang henggang pointed out the problem. Thank you very much for 2014.3.15
          }
 
          // Malloc the new memory for the pValue
          pValue = new wchar_t[MAXSIZE];
          memset(pValue, 0, sizeof(wchar_t) * MAXSIZE);
          wcscpy_s(pValue, MAXSIZE, test.pValue);
          return *this;
     }
 
     void Print()
     {
          wcout<<pValue<<endl;
     }
 
private:
     wchar_t *pValue; // The pointer points the memory
};
 
int main()
{
     CTest obj(L"obj");
     obj.Print();
 
     CTest obj2(L"obj2");
     obj2.Print();
     obj2 = obj;
     obj2.Print();
 
     obj2 = obj2;
     obj2.Print();
 
     return 0;
}

Especially in the implementation of overloaded assignment constructor need more attention, I also added comments in the code, you can seriously read the code, and then understand, if you don't understand can leave a message to me; Of course, if I understand something wrong, I also hope you can give me, we make progress together.

Copy some details of the constructor

1. Which of the following are the copy constructors


X::X(const X&);  
X::X(X);  
X::X(X&, int a=1);  
X::X(X&, int a=1, int b=2);

These details are also explained here, I also read from other people's blog, here also summarized. For a class X, if the first argument to a constructor is one of the following:

a) X&
b) const X&
c) volatile X&
d) const volatile X&

This function is a copy constructor if no other parameters or all other parameters have default values.

X::X(const X&);  //Is the copy constructor & NBSP; & have spent < br / > X::X(X&, int=1); //Is the copy constructor & NBSP; < br / > X::X(X&, int a=1, int b=2); //And of course the copy constructor

2. More than one copy constructor can exist in a class


class X
{
public:      
  X(const X&);      //Copy construct of const
  X(X&);            //Non-const copy construct
};

Note that you cannot use const X or volatile X objects for copy initialization if there is only one copy constructor with an argument of X& in a class. If a copy constructor is not defined in a class, the compiler automatically produces a default copy constructor. This default parameter may be X::X(const X&) or X::X(X&), whichever the compiler chooses based on the context. In my Visual Studio 2012, when multiple copy constructors are defined, the compiler will have warnings, but the program will still run correctly.

conclusion

This article summarizes the copy constructor and overloaded assignment operators, focusing on the timing of the invocation of the copy constructor and overloaded assignment operators. I don't use too much text to explain the deep - and shallow - copy problem that you like to summarize, but I think the code above is enough. Finally, he has been struggling for a long time of the problem is summed up in this way, he also thoroughly understand.


Related articles: