In general, we often copy copies of some data when we build member functions in a class template, and generally we do not advocate copying with memcpy because the types passed in in the class template can be built-in types or non-built types. Unless you type-extract before using memcpy in a member function, the consequences of its side effects can be terrifying. memcpy is a normal copy of the built-in type, while a shallow copy occurs when copying to a non-built-in type.
Below we can analyze the side effects of memcpy on non-built-in types through a simple sequential table program:
#include <iostream> #include <string>using namespace std;template<typename T> Class seqlist{public:seqlist () //constructor: _data (NULL), _size (0), _capacity (0) {}seqlist<t>& operator= (const seqlist<t>& s) //Assignment Overload {if (this != &s) {_data = new t[s._capacity];_size = s._size;_capacity=s._capacity;memcpy (_data, s._data, _size*sizeof (T));/*int i=0;for (i = 0; i < _size; i++) {_data[ I] = s._data[i];} */}return *this;} Seqlist (const seqlist<t>& s) //Copy Construction: _data (New t[s._capacity]), _size (s._size), _capacity (s._capacity) { memcpy (_data, s._data, _size*sizeof (T));/*int i = 0;for (i = 0; i < _size; i++) {_data[i] = s._data[i];} */}~seqlist () //destructor {if (_data != null) {delete[] _data;_size = 0;_capacity = 0;}} Public:void pushback (const t& d) {checkcapacity (); _data[_size] = d;_size++;} Public:void checkcapacity () //capacity Detection {if (_size == _capacity) {t *tmp = new t[2 * _capacity + 3];memcpy (tmp, _data, _size*sizeof (T));/*int i = 0;for (i = 0; i < _size; i++) {tmp[i] = _data[i];} */delete[] _data;_data = tmp;_capacity = 2 * _capacity + 3;}} Void print () {int i = 0;for (; i < _size; i++) {cout < < _data[i] << " ";} Cout << endl;} private:t*_data;int _size;int _capacity;};
The above is a simple sequential table of the template, when we put the copy construction in the templates, the assignment overload and capacity detection are implemented with memcpy, and we give the following test main function ( at this time we are using the built-in type ):
int main () {seqlist<int> s;s.pushback (1); S.pushback (2); S.pushback (3); S.pushback (4); S.print (); Seqlist<int> S3 (s); S.print (); Seqlist<int> s2;s2 = s;s2. Print (); System ("pause"); return 0;}
Here is the result of the operation:
650) this.width=650; "src=" Http://s4.51cto.com/wyfs02/M01/7D/E7/wKioL1bykzbjOKaRAABJWn1QQrQ244.png "title=" QQ picture 20160323205152.png "alt=" Wkiol1bykzbjokaraabjwn1qqrq244.png "/>
The program does not have an exception at this point, and when we change its test main function to string, the following is the case:
int main () {seqlist<string> S;s.pushback ("111"), S.pushback ("222"); S.pushback ("333"); S.pushback ("444"); s. Print (); Seqlist<string> S3 (s); S.print (); Seqlist<string> s2;s2 = s;s2. Print (); System ("pause"); return 0;}
What happens at this point is:
650) this.width=650; "src=" Http://s4.51cto.com/wyfs02/M01/7D/EB/wKiom1bylWbyvRYBAAB_i4kZtA0263.png "title=" QQ picture 20160323210358.png "alt=" Wkiom1bylwbyvrybaab_i4kzta0263.png "/>
That's right! The program crashed!
And no matter you use any of the above three member functions, the program will crash!
Since I am using VS2013, the program crashes after the cursor to the destructor, so it is relatively easy to think of a shallow copy of the situation ( because different versions of the compiler will have different conditions, so the analysis of this article is based on the VS2013 version )
The following is an example of capacity detection of this member function for a specific analysis, when we do the end of the plug, only when we insert more data than _capacity, will be allowed to increase capacity processing, because I give the initial capacity is three, so when the fourth insert will be entered into the increment function, Is the condition that occurs when you enter the capacity function:
650) this.width=650; "src=" Http://s3.51cto.com/wyfs02/M00/7D/E8/wKioL1bynCTQbVJTAAE7EXNiTf0951.png "title=" QQ picture 20160323213000.png "alt=" Wkiol1bynctqbvjtaae7exnitf0951.png "/>
Can see when go in after the execution of memcpy, _data Point is an address, and when I took this address to find a very magical phenomenon, this address put it unexpectedly is it:
650) this.width=650; "src=" Http://s3.51cto.com/wyfs02/M02/7D/EB/wKiom1bynP6jBQAwAAA3JOmF1RM326.png "title=" QQ picture 20160323213624.png "alt=" Wkiom1bynp6jbqawaaa3jomf1rm326.png "/>
Yes, it's _data's address!
Then I looked at the address of TMP, such as:
650) this.width=650; "src=" Http://s2.51cto.com/wyfs02/M02/7D/E8/wKioL1bynkbQGfUkAABHDzKGQ4Q822.png "title=" QQ picture 20160323213918.png "alt=" Wkiol1bynkbqgfukaabhdzkgq4q822.png "/>
Here it is basically clear that TMP and _data point to the same address, and this address is _data, so in the larger image above, when _data is released, TMP inside or _data address, is an already freed space address, and in the destructor you released a , so the program will crash at the destruction, which is also the side effect of memcpy--shallow copy phenomenon!
And when you let go of the For loop, and then comment out the memcpy, the problem is solved, in this case, because this is done with the assignment of string operator=.
One thing to note here is that because different versions of the compiler do not have the same structure for string, I have been emphasizing the environment of compilation, but the other versions may not have the same phenomenon, but the principle is not changed! Therefore, we should try our best to avoid using memcpy in the future class template.
If there are deficiencies in this article, please remind the reader in the place of the message, thank you!
Analysis of the side effects of using memcpy when building member functions in a class template