The difference and use of constructor, copy constructor and move constructor


Illustrated with class types in C++

Source code

class Moveable
{
    
    

public:
	Moveable() : i(new int(3)){
    
     //构造函数
		cout << "构造函数" << endl; 
	};
	~Moveable(){
    
    //析构函数
		delete i;//这里也没有必要判断i是否为空指针,然后再执行delete i,因为delete 一个指向空地址的指针变量 这里也不会报错,delete命令底层有优化。
		i = nullptr;//这里没必要把i置为空指针,因为i是对象中的非静态成员变量,对象生命周期结束后指针变量i生命周期也随着结束了。
	};
	Moveable(const Moveable & m) : i(new int(*m.i)){
    
    //拷贝构造函数
		cout << "调用拷贝构造函数" << endl;
	};
	Moveable(Moveable && m) : i(m.i){
    
    //移动构造函数
		cout << "调用移动构造函数" << endl;
		m.i = nullptr; 
	};
	int* i;
};

Constructor

constructor is written in the class without being displayed, and there will be an implicit constructor. For example: Moveable(){}; No parameters need to be passed. The attributes in the object are given random values. This is very dangerous. Therefore, generally
will override the constructor explicitly and initialize member variables in the constructor. Constructors support overloading.
When will the constructor be triggered?
Calling the constructor is automatically triggered when an object is created. For example: Moveable m;//will trigger the call to the constructor

copy constructor

copy constructor, written in the class without display, there will be an implicit copy constructor. For example:
Moveable(const Moveable & m) : i(m.i)){ };
When will the copy be called?
Moveable m;//Will trigger the call to the constructor
Moveable m1(m);//m is an lvalue, regardless of whether the compiler has optimized it or not. The copy constructor will be called
. That is, when another object is generated based on an existing object, the copy constructor will be triggered.

Shallow copy

Using the implicit copy constructor will cause a shallow copy problem, that is, the pointer member i in the new object and the old object both point to the same piece of heap memory. When one of the two objects releases the memory, it will be called delete i once.
When another object releases memory, delete i will also be called. It will happen that the same heap memory is released twice, which will cause program errors.
When delete i is executed for the first time, the pointer variable i in another object becomes a dangling pointer. That is, the memory pointed to by the pointer has been released (can no longer be accessed).
The implicit copy constructor is the shallow copy constructor. To avoid this situation, you generally need to override the copy constructor.

deep copy

is aimed at the shallow copy problem caused by the above implicit copy constructor. A deep copy is needed to solve it.
For example:
Moveable(const Moveable & m) : i(new int(*m.i)){...copy operation...};< a i=3> means that during the copying process, a piece of heap memory is reallocated to the pointer member attribute of the new object, and then in the function body, the content in the heap memory pointed to by the old object pointer member i is copied to the new object pointer. member in the heap memory pointed to by the member. This solves the problem of dangling pointers caused by shallow copying.

move constructor

move constructor is a new type of constructor introduced in C++11. There is no implicit move constructor. When you want to use it, you must explicitly define the move constructor in the class.
The formal parameter type of the move constructor is an rvalue reference type (also a new reference type in C++11, such as: T &&)
For example:
Moveable(Moveable && m) : i(m.i){ m.i = nullptr; //Move semantics }; Moveable &&: It is an rvalue reference type, In fact, it is no different from lvalue reference (Moveable &) when operating data. They both represent the alias of the object to be referenced. lvalue reference type: used to reference lvalues. Rvalue reference type: used to reference rvalues.




What is the significance of the mobile constructor?
The meaning of the move constructor is that when copying data, we do not want operations like deep copy to occur. If the content of the heap memory pointed to by the pointer member i is particularly large, then when performing a deep copy, low efficiency. Choosing to use the implicit copy constructor will cause the dangling pointer problem mentioned above.
In fact, the move constructor here can be regarded as a shallow copy constructor. Because the implicit copy constructor has been rewritten, the implicit shallow copy constructor is no longer provided in the class.
Therefore, when there is a deep copy constructor, I still want to use a shallow copy constructor, and I want to avoid the dangling pointer problem with shallow copy. Then the move constructor came into being.
The move constructor and the explicit deep copy constructor can coexist.
The difference between the move constructor and the default shallow copy constructor is that the parameter types are different (one is an lvalue reference type and the other is an rvalue reference type). The key difference is the move semantics within the function body.
Both move constructors and shallow copy constructors are stealing other people's content.
Analyze: the execution of the move constructor. First receive an rvalue, then assign it to the rvalue reference type variable m, and assign the old object pointer i to the new object pointer i. At this time, the member attribute pointer i of the new object also points to the heap memory pointed to by the member attribute i of the old object. Then in the function body, set the attribute pointer i of the old object to a null pointer. In this way, when the destructor is triggered at the end of the old object's life cycle, the heap memory will no longer be released. Therefore, the problem that the new object attribute pointer i will become a dangling pointer is avoided.
Generally, the rvalue passed into the move constructor will end its life cycle, or we will not use it again in the future. But when you still want the contents of its objects (especially objects that occupy large memory). We choose to use the move copy constructor, which is more efficient.
When will the move constructor be triggered?
Moveable m;
Moveable m2(std::move(m));//The only function of std::move(m) is to force conversion of m is an rvalue. Then the move constructor is called when m2 is created.
注意:When there is no move constructor defined in the class, this statement Moveable m2(std::move(m)); will call the copy constructor.
Therefore, the move constructor is called first. If not, the copy constructor is called.
std::move(); //The function is also a new feature in C++11. Its function is to convert an lvalue into an rvalue.

Guess you like

Origin blog.csdn.net/adminstate/article/details/134926730