C++ copy constructor and assignment operator (transfer)

This article mainly introduces the difference between the copy constructor and the assignment operator, as well as when to call the copy constructor and under what circumstances to call the assignment operator. Finally, the problem of deep copy and shallow copy is briefly analyzed.

copy constructor and assignment operator

By default (not defined by the user, but not explicitly deleted), the compiler will automatically and implicitly generate a copy constructor and assignment operator. But the user can use deleteto specify not to generate copy constructors and assignment operators, such objects cannot be passed by value and cannot be assigned.

class Person
{
public:

    Person(const Person& p) = delete;

    Person& operator=(const Person& p) = delete;

private:
    int age;
    string name;
};

The class defined above Personexplicitly deletes the copy constructor and assignment operator. Wherever the copy constructor or assignment operator needs to be called, it will prompt _the function cannot be called, it is a deleted function_.
Another point to note is that the copy constructor must pass parameters by reference . This is because, when passing by value to a function, the copy constructor is called to generate the actual parameters of the function. If the parameter of the copy constructor is still by value, it will be called in an infinite loop until the function's stack overflows.

when to call

The behavior of the copy constructor and assignment operator is similar, both copy the value of one object to another object; but the result is somewhat different, the copy constructor uses the value of the passed object to generate a new instance of the object, while The assignment operator copies the value of an object to an existing instance . This difference can be easily distinguished from the names of the two. The copy constructor is also a constructor, so its function is to create a new object instance; the assignment operator is to perform a certain operation to convert an object's The value is copied to another object (which already exists). Whether a copy constructor or an assignment operator is called depends on whether a new object instance is generated. If a new object instance is generated, the copy constructor is called; if not, it assigns an existing object, and the assignment operator is called .

The main scenarios for calling the copy constructor are as follows:

  • Objects are passed to functions as parameters of functions by value. 
  • Object as the return value of a function, returned from a function by value
  • Use one object to initialize another object

code show as below:

class Person
{
public:
    Person(){}
    Person(const Person& p)
    {
        cout << "Copy Constructor" << endl;
    }

    Person& operator=(const Person& p)
    {
        cout << "Assign" << endl;
        return *this;
    }

private:
    int age;
    string name;
};

void f(Person p)
{
    return;
}

Person f1()
{
    Person p;
    return p;
}

int main()
{
    Person p;
    Person p1 = p;    // 1
    Person p2;
    p2 = p;           // 2
    f(p2);            // 3

    p2 = f1();        // 4

    Person p3 = f1(); // 5

    getchar();
    return 0;
}

The above code defines a class Personthat explicitly defines the copy constructor and assignment operator. Then two functions are defined: f, pass in the Person object f1by value; , return the Person object by value. In mainsimulating 5 scenarios, the test calls the copy constructor or the assignment operator. The execution result is as follows:

 

 

 

 

 

 

analyse as below:

  1. This is using "=", but actually using the object p to create a new object p1. That is, a new object is generated, so the copy constructor is called.
  2. First declare an object p2, and then use the assignment operator "=" to copy the value of p to p2, obviously calling the assignment operator to assign a value to an existing object.
  3. The object p2 is passed into the function f by value passing, and the copy constructor is called to construct an actual parameter available to the function f.
  4. This statement calls both the copy constructor and the assignment operator. The function f1 returns a Person object by value. When returning, the copy constructor is called to create a temporary object tmp as the return value; after returning, the assignment operator is called to assign the temporary object tmp to p2.
  5. According to the explanation of 4, it should first call the copy constructor to create a temporary object; then call the copy constructor to create a new object p3 using the temporary object just created, that is, the copy constructor will be called twice. However, the compiler is not that stupid, it should have directly called the copy constructor to create the object p3 with the return value.

deep copy, shallow copy

说到拷贝构造函数,就不得不提深拷贝和浅拷贝。通常,默认生成的拷贝构造函数和赋值运算符,只是简单的进行值的复制。例如:上面的Person类,字段只有intstring两种类型,这在拷贝或者赋值时进行值复制创建的出来的对象和源对象也是没有任何关联,对源对象的任何操作都不会影响到拷贝出来的对象。反之,假如Person有一个对象为int *,这时在拷贝时还只是进行值复制,那么创建出来的Person对象的int *的值就和源对象的int *指向的是同一个位置。任何一个对象对该值的修改都会影响到另一个对象,这种情况就是浅拷贝。

深拷贝和浅拷贝主要是针对类中的指针动态分配的空间来说的,因为对于指针只是简单的值复制并不能分割开两个对象的关联,任何一个对象对该指针的操作都会影响到另一个对象。这时候就需要提供自定义的深拷贝的拷贝构造函数,消除这种影响。通常的原则是:

  • 含有指针类型的成员或者有动态分配内存的成员都应该提供自定义的拷贝构造函数
  • 在提供拷贝构造函数的同时,还应该考虑实现自定义的赋值运算符

对于拷贝构造函数的实现要确保以下几点:

  • 对于值类型的成员进行值复制
  • 对于指针和动态分配的空间,在拷贝中应重新分配分配空间
  • 对于基类,要调用基类合适的拷贝方法,完成基类的拷贝

总结

  • 拷贝构造函数和赋值运算符的行为比较相似,却产生不同的结果;拷贝构造函数使用已有的对象创建一个新的对象,赋值运算符是将一个对象的值复制给另一个已存在的对象。区分是调用拷贝构造函数还是赋值运算符,主要是否有新的对象产生。
  • 关于深拷贝和浅拷贝。当类有指针成员或有动态分配空间,都应实现自定义的拷贝构造函数。提供了拷贝构造函数,最后也实现赋值运算符。

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326314986&siteId=291194637