Copy constructor, copy assignment operator, destructor, move constructor, move assignment operator in C++ (three/five rules)

1 Introduction

The three-to-five rule is an operation function for members of classes and class objects in C++.

The rule of three refers to: copy constructor, copy assignment operator, and destructor.

The five rules are added on the basis of the three rules: move constructor, move assignment operator.

2. Copy constructor

Definition: If the first parameter of the constructor is a reference to its own class type, and any additional parameters have default values, the constructor is a copy constructor. If no copy constructor is defined for the class, the compiler will generate a copy constructor by default.

Purpose: Define an existing class object to initialize the member initialization process of a new object of this class.

eg:

class Foo
{
  public:
    Foo(); //默认构造函数
    Foo(const Foo&);    //拷贝构造函数
    //.....    
}

The difference between copy initialization and direct initialization:

string dots(9,'.');    //直接初始化
string s(dots);        //直接初始化
string s2 = dots;      //拷贝初始化
stirng null_book = "hello world";    //拷贝初始化
string nines=string("9",'.');    //拷贝初始化

Direct initialization means that the compiler chooses the constructor that best matches the parameters we provide for initialization. Copy-initialization refers to copying the right-hand operand into the object being created . Pay attention to two points: the object being created, and the assignment symbol =.

There are several situations in which the copy constructor is called:

(1) Use the "=" symbol to define variables.

(2) Pass an object as an actual parameter to a formal parameter of a non-reference type.

(3) Return an object from a function whose return type is a non-reference type.

(4) Initialize elements in an array or members in an aggregate class with a list of curly braces.

3. Copy assignment operator

Definition: The function that overloads the class object assignment operator "=" is the copy assignment operator. Simply put, it is an overloaded operator function. The form is as follows:

class Foo
{
  public:
    Foo(); //默认构造函数
    Foo(const Foo&);    //拷贝构造函数
    Foo& operator=(const Foo&);    //拷贝赋值运算符
    //.....    
}

Foo& Foo::operator=(const Foo& org)
{
    //.....
    return *this;    //注意:拷贝赋值运算符应该返回一个指向其左侧预算对象的引用this。
}

4. Destructor

Definition: Release the resources used by the object and destroy the non-static data members of the object.

class Foo
{
    public:
    Foo(); //默认构造函数
    Foo(const Foo&);    //拷贝构造函数
    Foo& operator=(const Foo&);    //拷贝赋值运算符
    ~Foo();    //析构函数
    int* ps;
    //.....    
};
Foo::~Foo()
{
    delete ps; 
}

Note: There can be multiple constructors in a class, but only one destructor. A smart pointer is a class type and has a destructor, so it will be automatically destroyed in the destructor phase, and does not need to be released manually. Generally, the heap memory allocated manually by new and malloc is released.

5. When to define the copy constructor and copy assignment operator?

In general, if a class needs to define a destructor, the class needs to define: a copy constructor and a copy assignment operator.

In other words, if the members of the class contain pointer member variables, the class needs to define: destructor, copy constructor, and copy assignment operator. (If there are no assignment and initialization operations between the objects of the class, then there is no need to define a copy constructor or copy assignment operator, and the compiler should be prevented from generating default copy and assignment functions. The way to prevent this is to add Above=delete. But in the case of defining a destructor, it is still necessary to define a destructor to release memory.)

class Foo
{
    public:
    Foo(); //默认构造函数
    Foo(const Foo&)=delete;    //阻止类对象的拷贝
    Foo& operator=(const Foo&)=delete;    //阻止类对象赋值
    ~Foo();    //析构函数
    //.....    
};

6. Move constructor, move assignment operator

 Simply put, it is to move the management right of an original object to another new object. The original object can no longer be used, because the management right has been moved to the new object! ! !

The advantage of the move constructor and move assignment operator is that the original object data is directly moved to the new object without deep copying the data, which improves efficiency.

The demo is as follows:

#include <iostream>
#include <string>

class SaleItem
{
    public:
        //1 如果有多个构造函数时,仍需要编译器默认的构造函数,则可以在声明时,在()后面加default,表示使用系统生成的默认构造函数。
        //1 这种在声明时,写=default,表示构造函数为类的内联函数;如果不想用内联函数,在声明和定义分开写。
        //1 默认的构造函数的形参为空。
        SaleItem(){m_name = new int(10);};

        //2 拷贝构造函数用于类类型对象的初始化操作。如果不定义会默认生成。

        SaleItem(const SaleItem&);

        //3、拷贝赋值运算符
        SaleItem& operator=(const SaleItem& orig);

        //4、析沟函数
        ~SaleItem(){if(m_n1 != NULL){delete m_name;m_name = NULL;} std::cout << " ~~~~free memory " << std::endl;};
    void PrintMem(){std::cout << "m_n1 " << m_n1 << " m_n2 " << m_n2  << " m_name "<< *m_name << std::endl;}

        //5、移动构造函数
        SaleItem(SaleItem &&orig);

        //6、移动赋值函数
        SaleItem& operator=(SaleItem&& orig);

    int m_n1=10; 
    int m_n2 = 1;
    int *m_name = NULL;

};
SaleItem::SaleItem(const SaleItem& orig)
{
    m_n1 = orig.m_n1+2;
    m_n2 = orig.m_n2 *2;
    auto newName = new int(*orig.m_name);   //创建临时变量保存数据并开辟内存
    if(m_name != NULL)
    {
        delete m_name;  //释放原内存的释放
        m_name = NULL;
    }
    m_name = newName;
    std::cout << "Copy Constructor " << std::endl;
}

SaleItem& SaleItem::operator=(const SaleItem& orig)
{
    std::cout << "Copy Assignment======= " << std::endl;
    auto newName = new int(*orig.m_name);   //创建临时变量保存待赋值数据并开辟内存,
    if(m_name != NULL)
    {
        delete m_name;  //释放原内存的释放
        m_name = NULL;
    }
    m_name = newName;
    return *this;
}

 SaleItem::SaleItem(SaleItem &&orig)
 {
    m_name = NULL;
    m_name = orig.m_name;
    orig.m_name = NULL;
    std::cout << "Move Constructor " << std::endl;
 }

 SaleItem& SaleItem::operator=(SaleItem&& orig)
{
    m_name = NULL;
    m_name = orig.m_name;
    orig.m_name = NULL;
    std::cout << "Move Assignment======= " << std::endl;
    return *this;
}

int main(int argc,char** argv)
{
    SaleItem obj1;
    SaleItem obj2(obj1);    //拷贝构造函---将类对象作为另一个函数的形参
    obj2.PrintMem();
    
    SaleItem obj3 = obj1;   //拷贝构造函----使用已存在的对象赋值给类新创建的对象!!!
    obj3.PrintMem();

    obj3 = obj1;            //拷贝赋值运算符-----使用已存在的对象赋值给另一个已存在的对象!!!

    SaleItem objMo(std::move(obj1));    //移动构造函数

    SaleItem objAssg;
    objAssg = std::move(obj3);          //移动赋值运算符
    return 0;
}

The result of the operation is as follows:

 Additional:

 The blogs you can refer to are as follows:

C++_Detailed Explanation of Copy Assignment Operator_Introduction and Simple_Illustrated_Dancing With Bugs Blog-CSDN Blog_Copy Assignment Operator Implementation

Detailed explanation of copy constructor and assignment constructor in C++_m0_60150025's blog-CSDN blog_c++ copy constructor and copy assignment function

Mobile Copy Constructor and Mobile Assignment Constructor - Li Zhaolong's Blog - Blog Garden (cnblogs.com)

New features of C++11: mobile constructor and mobile assignment_simple l's blog-CSDN blog_mobile constructor and mobile assignment

C++ std::move Principle Implementation and Usage Summary_ppipp1109's Blog-CSDN Blog_stdmove

std::move() and mobile constructor_std::move constructor subclass_Thin Pikachu's Blog-CSDN Blog

Guess you like

Origin blog.csdn.net/hanxiaoyong_/article/details/128793479