A simple introduction to C + + move constructor and move statement first looks at a small example:
#include <iostream>#include<cstring>#include<cstdlib>#include<vector>using namespacestd;intMain () {stringSt ="I Love Xing"; Vector<string>VC; Vc.push_back (Move (ST)); cout<<vc[0]<<Endl; if(!st.empty ()) cout<<st<<Endl; return 0;}
The result is:
#include <iostream>#include<cstring>#include<cstdlib>#include<vector>using namespacestd;intMain () {stringSt ="I Love Xing"; Vector<string>VC; Vc.push_back (ST); cout<<vc[0]<<Endl; if(!st.empty ()) cout<<st<<Endl; return 0;}
The result is:
The only difference between these two small programs is that when the Vc.push_back () is called to insert a string into the container, the first code uses the move statement, and the second code does not use the move statement. The result of the output difference is also obvious, the first paragraph of the code, the original string St has been empty, and the second paragraph of the Code, the original string St content has not changed .
Well, remember the difference between the output results of the two ends of the code. Let's take a brief look at the move constructor.
Before we introduce the move constructor, we'll look at the copy constructor.
As we all know, C + + in three cases will invoke the copy constructor (there may be a fault), the first case is the function of the real union, the second case is when the function returns, the function stack of the object will be copied to the return of the function, the third case is to initialize another object with one object will also call the copy constructor.
In addition to these three cases the copy constructor is called, and if an object is assigned to another object, the overloaded assignment operator function is called back.
Whether it is a copy constructor or an overloaded assignment operator function, I remember that when I was in C + + class, the teacher stressed, we must pay attention to the shallow copy of the pointer.
Here's a simple recall. Shallow copy problem in copy constructor
First look at a shallow copy of the code
#include <iostream>#include<cstring>#include<cstdlib>#include<vector>using namespacestd;classstr{ Public: Char*value; STR (Chars[]) {cout<<"Call constructor ..."<<Endl; intLen =strlen (s); Value=New Char[Len +1]; memset (Value,0, Len +1); strcpy (value,s); } str (str&v) {cout<<"call copy constructor ..."<<Endl; This->value =V.value; } ~Str () {cout<<"Call destructor ..."<<Endl; if(Value! =NULL) delete[] value; }};intMain () {CharS[] ="I Love BIT"; Str*a =NewStr (s); Str*b =NewSTR (*a); Delete A; cout<<"the string in the B object is:"<<b->value<<Endl; Delete B; return 0;}
The output is:
The first result is not expected, and we want the string in the B object to be the I love bit but the output is empty, because B->value and A->value point to the same area of memory, when delete A is removed, the memory area is retracted, so B- >value access to that block of memory is actually inappropriate, and although I run the program does not crash, but there is a risk of the program crash, because when the delete B, the memory area was released once, two times to release the same piece of memory, it is quite dangerous.
We use Valgrind to check, found that quite a lot of memory errors Ah!
One of them is a invalid free, that is, when you delete B, the destructor is called, and the space is released once again.
So what should be written about deep replication?
The code is as follows:
#include <iostream>#include<cstring>#include<cstdlib>#include<vector>using namespacestd;classstr{ Public: Char*value; STR (Chars[]) {cout<<"Call constructor ..."<<Endl; intLen =strlen (s); Value=New Char[Len +1]; memset (Value,0, Len +1); strcpy (value,s); } str (str&v) {cout<<"call copy constructor ..."<<Endl; intLen =strlen (V.value); Value=New Char[Len +1]; memset (Value,0, Len +1); strcpy (Value,v.value); } ~Str () {cout<<"Call destructor ..."<<Endl; if(Value! =NULL) {delete[] value; Value=NULL; } }};intMain () {CharS[] ="I Love BIT"; Str*a =NewStr (s); Str*b =NewSTR (*a); Delete A; cout<<"the string in the B object is:"<<b->value<<Endl; Delete B; return 0;}
The result is:
This time we have achieved the desired effect, and, with valgrind detection, found that there is no memory error!
So, when writing the copy constructor, remember to pay attention to the shallow copy of the pointer !
OK, review the copy constructor, and go back to the move constructor.
Sometimes we come across a situation where we initialize object B with Object A, and then object A is not used, but the space of object A is still there (before the destruction), since the copy constructor is actually copying the contents of the A object to B, then why can't we just use a space? This avoids the allocation of new space and greatly reduces the cost of construction. This is the original intention of the mobile constructor design.
The following figure illustrates the difference between a copy constructor and a moving constructor.
Did you see that?
The popular explanation is that in the copy constructor, for pointers, we must take deep copy, and in the move constructor, for pointers, we use shallow copy .
But as mentioned above, the shallow duplication of pointers is very dangerous. Yes, it is indeed dangerous, and through the above example, we can also see that the shallow copy is dangerous because the two pointers together point to a piece of memory space, if the first pointer to release it, the other pointer point is not legal. So we just have to avoid the first pointer freeing up space. The way to avoid this is to set the first pointer (such as a->value) to NULL, so that when the destructor is called, because there are statements that determine whether it is null, it does not reclaim the space that A->value points to (and is also b-> The space that value points to)
So we can modify the code of the copy constructor above:
#include <iostream>#include<cstring>#include<cstdlib>#include<vector>using namespacestd;classstr{ Public: Char*value; STR (Chars[]) {cout<<"Call constructor ..."<<Endl; intLen =strlen (s); Value=New Char[Len +1]; memset (Value,0, Len +1); strcpy (value,s); } str (str&v) {cout<<"call copy constructor ..."<<Endl; This->value =V.value; V.value=NULL; } ~Str () {cout<<"Call destructor ..."<<Endl; if(Value! =NULL) delete[] value; }};intMain () {CharS[] ="I Love BIT"; Str*a =NewStr (s); Str*b =NewSTR (*a); Delete A; cout<<"the string in the B object is:"<<b->value<<Endl; Delete B; return 0;}
The result is:
Modified copy constructor, using shallow copy, but the result can still achieve the effect we want, the key is in the copy constructor, finally we put v.value to null, so at the time of the destruction of a, it will not reclaim the A->value point of memory space.
So in the process of initializing B with a, we actually reduce the amount of open memory, and the construction cost is reduced.
However, it is important to note that if we use a to initialize B, we do not need a, preferably a destructor after initialization is complete. If we have initialized B with a, we still have to operate on a, and this shallow copy method is inappropriate.
So C + + introduced a mobile constructor, specifically dealing with this, with a initialization B, will be a destruction of the case.
*************************************************************
* * The parameters of the move constructor are different from the copy constructor, and the parameter of the copy constructor is an lvalue reference, but the initial value of the move constructor is an rvalue reference. (For rvalue references you can look at my previous article, or find other materials). This means that the parameter of the move constructor is either an rvalue or a reference to the dead value. That is, the move constructor is called only when a right value is used, or if the dead value is initialized to another object. And that move statement is to turn an lvalue into a dead value.
The most common application of mobile constructors is in STL
Give us a code to verify the difference between using move and not working with move.
#include <iostream>#include<cstring>#include<cstdlib>#include<vector>using namespacestd;classstr{ Public: Char*str; STR (Charvalue[]) {cout<<"Common Constructors ..."<<Endl; STR=NULL; intLen =strlen (value); STR= (Char*)malloc(Len +1); memset (str,0, Len +1); strcpy (Str,value); } Str (ConstSTR &s) {cout<<"copy constructor ..."<<Endl; STR=NULL; intLen =strlen (S.STR); STR= (Char*)malloc(Len +1); memset (str,0, Len +1); strcpy (STR,S.STR); } str (str&&s) {cout<<"Move constructor ..."<<Endl; STR=NULL; STR=S.str; S.str=NULL; } ~Str () {cout<<" Destructors"<<Endl; if(str! =NULL) { Free(str); STR=NULL; } }};intMain () {CharValue[] ="I Love ZX"; STR s (value); Vector<Str>vs; //Vs.push_back (Move (s));Vs.push_back (s); cout<<vs[0].str<<Endl; if(S.str! =NULL) cout<<s.str<<Endl; return 0;}
If you find it useful to you, please let me have one.
Simple Introduction to C + + move constructors and move statements