[C++11]_[初级]_[使用std::move移动对象资源]
2016-02-25 11:59
429 查看
场景:
C++ 标准库使用比如vector::push_back 等这类函数时,会对参数的对象进行复制,连数据也会复制.这就会造成对象内存的额外创建, 本来原意是想把参数push_back进去就行了.C++11 提供了std::move 函数来把左值转换为xrvalue, 而且新版的push_back也支持&&参数的重载版本,这时候就可以高效率的使用内存了.
对指针类型的标准库对象并不需要这么做.
参考:
1. Move Constructors and Move Assignment Operators (C++)2. std::move
说明:
std::move(t) 用来表明对象t 是可以moved from的,它允许高效的从t资源转换到lvalue上.注意,标准库对象支持moved from的左值在moved 之后它的对象原值是有效的(可以正常析构),但是是unspecified的,可以理解为空数据,但是这个对象的其他方法返回值不一定是0,比如size().所以,moved from 之后的对象最好还是不要使用吧?(如有不正确理解,请告知)
对本身进行move,并赋值给本身是undefined的行为.
[code]std::vector<int> v = {2, 3, 3}; v = std::move(v); // undefined behavior
std::move 的函数原型.
[code]/** * @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<typename _Tp> constexpr typename std::remove_reference<_Tp>::type&& move(_Tp&& __t) noexcept { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
结构体 remove_reference 的原型,就是重载了多个结构体模板来获取原类型 type.
[code]/// remove_reference template<typename _Tp> struct remove_reference { typedef _Tp type; }; template<typename _Tp> struct remove_reference<_Tp&> { typedef _Tp type; }; template<typename _Tp> struct remove_reference<_Tp&&> { typedef _Tp type; };
例子
以下用两个例子来说明std::move的用法.例子1
– 原lvalue值被moved from之后值被转移,所以为空字符串.– 摘录自cppreference
[code]void TestSTLObject() { std::string str = "Hello"; std::vector<std::string> 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"; }
输出:
[code]After copy, str is "Hello" After move, str is "" The contents of the vector are "Hello", "Hello"
例子2
– 自定义自己的类对象支持moved from 操作,需要实现 Move Constructors and Move Assignment Operators[code] #include <iostream> #include <stdio.h> #include <utility> #include <vector> #include <string> 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<std::string> 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<MemoryBlock> 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<MemoryBlock> 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; }
输出:
1. 注意,第一个函数每个对象多调用了拷贝构造函数,多创建了一次,而使用了move操作的只是移动了资源
2. 注意,vector即使 push_back 第二个对象时,会移动第一个对象,很奇怪,如果你把注释去掉的话,会发现资源 Moving 很多次,这是 vector 实现影响了,比较清楚的看出来 Move 的特性的就是 push_back 一个参数.
3. 注意,g++ 4.8.1 的 vector push_back 多个对象时优化的没 vs 好,vs 是调用 Move 构造器,而 g++ 是调用 Copy 构造器,你会发现拷贝构造函数会调用很多次.
[code]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.
相关文章推荐
- c++实现大数加减乘除四则运算
- log4cpp学习
- c/c++内存对齐详解
- 图片整理
- C++11新特性总结(枚举+继承+左右值引用+变长模板)
- 使用C++实现虚幻4通电游戏
- 挑7
- 字符串最后一个单词的长度
- c++中的左值与右值
- strcmpi
- C++Primer第5版读书笔记(第2章)
- C/C++变量命名规则,个人习惯总结
- [置顶] c语言入门项目之4.0——利用for循环求1/2-2/3+3/4-...+19/20
- C++学习笔记:多态的实现原理
- c++11随机数产生器default_random_engine
- C语言基本数据类型简介
- 浅谈 C++ 中的 new/delete 和 new[]/delete[]
- C++ 基础教程(结合虚幻4)
- C++11之生产者消费者
- C语言知识整理(一)