[C ++ 11] _ [primary] _ [Use std: move object resource]
Scenario: When the C ++ standard library uses functions such as vector: push_back, the parameter objects are copied and even data is copied. this will cause additional Object Memory creation. The original intention was to put the parameter push_back in. C ++ 11 provides the std: move function to convert the left value to xrvalue, and the new push_back version also supports the & Parameter overload version, at this time, you can use the memory efficiently. this is not required for standard library objects of pointer type. note: std: move (t) indicates that the object t can be moved from. It allows efficient conversion from the t resource to the lvalue. note: The original value of a standard library object that supports the left value of moved from after moved is valid (normal analysis can be performed), but it is unspecified and can be understood as null data, however, the return value of other methods of this object is not necessarily 0, such as size (). therefore, it is best not to use the objects after moved from? (If you do not understand it correctly, please inform me) move itself and assign it to itself as undefined behavior.
std::vector
v = {2, 3, 3};v = std::move(v); // undefined behavior
Std: the prototype of the move function.
/** * @brief Convert a value to an rvalue. * @param __t A thing of arbitrary type. * @return The parameter cast to an rvalue-reference to allow moving it.*/template
constexpr typename std::remove_reference<_Tp>::type&& move(_Tp&& __t) noexcept { return static_cast
::type&&>(__t); }
The prototype of the struct remove_reference is to overload multiple struct templates to obtain the original type.
/// remove_referencetemplate
struct remove_reference { typedef _Tp type; };template
struct remove_reference<_Tp&> { typedef _Tp type; };template
struct remove_reference<_Tp&&> { typedef _Tp type; };
Example
Here are two examples to illustrate std: move usage.
Example 1
-The original lvalue is transferred after being moved from, so it is an empty string.
-Extracted from cppreference
void TestSTLObject(){ std::string str = "Hello"; std::vector
v; // uses the push_back(const T&) overload, which means // we'll incur the cost of copying str v.push_back(str); std::cout << "After copy, str is \"" << str << "\"\n"; // uses the rvalue reference push_back(T&&) overload, // which means no strings will be copied; instead, the contents // of str will be moved into the vector. This is less // expensive, but also means str might now be empty. v.push_back(std::move(str)); std::cout << "After move, str is \"" << str << "\"\n"; std::cout << "The contents of the vector are \"" << v[0] << "\", \"" << v[1] << "\"\n";}
Output:
After copy, str is "Hello"After move, str is ""The contents of the vector are "Hello", "Hello"
Example 2
-Custom class objects support the moved from operation. You need to implement the Move Constructors and Move Assignment Operators operation.
#include
#include
#include
#include
#include
class MemoryBlock{public: // Simple constructor that initializes the resource. explicit MemoryBlock(size_t length) : _length(length) , _data(new int[length]) { std::cout << "In MemoryBlock(size_t). length = " << _length << "." << std::endl; } // Destructor. ~MemoryBlock() { std::cout << "In ~MemoryBlock(). length = " << _length << "."; if (_data != nullptr) { std::cout << " Deleting resource."; // Delete the resource. delete[] _data; } std::cout << std::endl; } // Copy constructor. MemoryBlock(const MemoryBlock& other) : _length(other._length) , _data(new int[other._length]) { std::cout << "In MemoryBlock(const MemoryBlock&). length = " << other._length << ". Copying resource." << std::endl; std::copy(other._data, other._data + _length, _data); } // Copy assignment operator. MemoryBlock& operator=(const MemoryBlock& other) { std::cout << "In operator=(const MemoryBlock&). length = " << other._length << ". Copying resource." << std::endl; if (this != &other) { // Free the existing resource. delete[] _data; _length = other._length; _data = new int[_length]; std::copy(other._data, other._data + _length, _data); } return *this; } // Retrieves the length of the data resource. size_t Length() const { return _length; } // Move constructor. MemoryBlock(MemoryBlock&& other) : _data(nullptr) , _length(0) { std::cout << "In MemoryBlock(MemoryBlock&&). length = " << other._length << ". Moving resource." << std::endl; // Copy the data pointer and its length from the // source object. _data = other._data; _length = other._length; // Release the data pointer from the source object so that // the destructor does not free the memory multiple times. other._data = nullptr; other._length = 0; } // Move assignment operator. MemoryBlock& operator=(MemoryBlock&& other) { std::cout << "In operator=(MemoryBlock&&). length = " << other._length << "." << std::endl; if (this != &other) { // Free the existing resource. delete[] _data; // Copy the data pointer and its length from the // source object. _data = other._data; _length = other._length; // Release the data pointer from the source object so that // the destructor does not free the memory multiple times. other._data = nullptr; other._length = 0; } return *this; }private: size_t _length; // The length of the resource. int* _data; // The resource.};void TestSTLObject(){ std::string str = "Hello"; std::vector
v; // uses the push_back(const T&) overload, which means // we'll incur the cost of copying str v.push_back(str); std::cout << "After copy, str is \"" << str << "\"\n"; // uses the rvalue reference push_back(T&&) overload, // which means no strings will be copied; instead, the contents // of str will be moved into the vector. This is less // expensive, but also means str might now be empty. v.push_back(std::move(str)); std::cout << "After move, str is \"" << str << "\"\n"; std::cout << "The contents of the vector are \"" << v[0] << "\", \"" << v[1] << "\"\n";}void TestMyObjectWithoutUseMove(){ std::vector
v; MemoryBlock mb1(25); // MemoryBlock mb2(75); // MemoryBlock mb3(50); v.push_back(mb1); //v.push_back(mb2); //v.insert(v.begin() + 1, mb3);}void TestMyObjectWithUseMove(){ std::vector
v; MemoryBlock mb1(25); // MemoryBlock mb2(75); // MemoryBlock mb3(50); v.push_back(std::move(mb1)); //v.push_back(MemoryBlock(75)); //v.insert(v.begin() + 1, MemoryBlock(50));}int main(int argc, char const *argv[]){ //TestSTLObject(); TestMyObjectWithoutUseMove(); std::cout << "......................................." << std::endl; TestMyObjectWithUseMove(); return 0;}
Output:
1. Note that each object of the first function calls a copy constructor and creates one more object. The move operation only moves resources.
2. note: Even if push_back is the second object, the vector will move the first object. It is strange that if you remove the annotation, you will find that the resource is Moving many times, which is affected by the implementation of the vector, it is clear that the Move feature is a push_back parameter.
3. note: When multiple objects in vector push_back of g ++ 4.8.1 are optimized, vs is used to call the Move constructor, while g ++ is used to call the Copy constructor, you will find that the copy constructor will be called many times.
In MemoryBlock(size_t). length = 25.In MemoryBlock(const MemoryBlock&). length = 25. Copying resource.In ~MemoryBlock(). length = 25. Deleting resource.In ~MemoryBlock(). length = 25. Deleting resource........................................In MemoryBlock(size_t). length = 25.In MemoryBlock(MemoryBlock&&). length = 25. Moving resource.In ~MemoryBlock(). length = 0.In ~MemoryBlock(). length = 25. Deleting resource.