场景:

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的行为.

std::vector v = {2, 3, 3};

v = std::move(v); // undefined behavior

std::move 的函数原型.

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.

/// 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

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"; }

输出:

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


#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 实现影响了, 这个是因为push_back引发vector长度增长导致其内存重新分配,原来vector中的对象都被移动到新分配的内存上,所以会多次调用move构造函数,如果事先reverse,就不会发生这种情况.比较清楚的看出来 Move 的特性的就是 push_back 一个参数.
3. 注意,g++ 4.8.1 的 vector push_back 多个对象时优化的没 vs 好,vs 是调用 Move 构造器,而 g++ 是调用 Copy 构造器,你会发现拷贝构造函数会调用很多次.

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.

**转自 https://blog.csdn.net/infoworld/article/details/50736633**

std::move 移动对象资源的更多相关文章

  1. C++ 11 右值引用以及std::move

    转载请注明出处:http://blog.csdn.net/luotuo44/article/details/46779063 新类型: int和int&是什么?都是类型.int是整数类型,in ...

  2. C++ 11 左值,右值,左值引用,右值引用,std::move, std::foward

    这篇文章要介绍的内容和标题一致,关于C++ 11中的这几个特性网上介绍的文章很多,看了一些之后想把几个比较关键的点总结记录一下,文章比较长.给出了很多代码示例,都是编译运行测试过的,希望能用这些帮助理 ...

  3. 右值引用和std::move函数(c++11)

    1.对象移动 1)C++11新标准中的一个最主要的特性就是移动而非拷贝对象的能力 2)优势: 在某些情况下,从旧内存拷贝到新内存是不必要的,此时对对象进行移动而非拷贝可以提升性能 有些类如IO类或un ...

  4. [转载]如何在C++03中模拟C++11的右值引用std::move特性

    本文摘自: http://adamcavendish.is-programmer.com/posts/38190.htm 引言 众所周知,C++11 的新特性中有一个非常重要的特性,那就是 rvalu ...

  5. 移动构造和移动赋值与std::move

    ------------------------------------移动构造------------------------------------------ 传统的深拷贝深赋值 对于类中,含有 ...

  6. 左值 lvalue,右值 rvalue 和 移动语义 std::move

    参考文章: [1] 基础篇:lvalue,rvalue和move [2] 深入浅出 C++ 右值引用 [3] Modern CPP Tutorial [4] 右值引用与转移语义 刷 Leetcode ...

  7. C++ 11中的右值引用以及std::move

    看了很多篇文章,现在终于搞懂了C++ 中的右值以及std::move   左值和右值最重要的区别就是右值其实是一个临时的变量 在C++ 11中,也为右值引用增加了新语法,即&&   比 ...

  8. C++11右值引用和std::move语句实例解析

    关键字:C++11,右值引用,rvalue,std::move,VS 2015 OS:Windows 10 右值引用(及其支持的Move语意和完美转发)是C++0x将要加入的最重大语言特性之一.从实践 ...

  9. c++ 11 移动语义、std::move 左值、右值、将亡值、纯右值、右值引用

    为什么要用移动语义 先看看下面的代码 // rvalue_reference.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #includ ...

随机推荐

  1. HTML框架、列表、表格

    本章内容一.列表1.有序列表ol <ol> <li></li> </ol>type的值有3个 默认为1(阿拉伯数字), 还有A/a(大小写字母),I/i ...

  2. isPlainObject的解释

    参考: http://www.365mini.com/page/jquery_isplainobject.htm 先来看下使用案例: //在当前页面内追加换行标签和指定的HTML内容 function ...

  3. 把Swift中的String转成NSString ,获取NSString的方法

    1.0 在Swift中的 String 并没有提供什么方法,直接把它自身转变成 Int .Float 等,而在OC中我们就可以这样  "123".integerValue 来获取它 ...

  4. UITableView性能的优化()

    1.0 使用不透明视图 不透明的视图可以极大地提高渲染的速度. 2.0 不要重复创建不必要的cell 也就是我们常说的 循环利用机制 (建立缓冲池) 3.0  减少视图的数目 4.0  不要做多余的绘 ...

  5. jdk动态代理和cglib动态代理

    参考: http://www.importnew.com/22015.html Java动态代理 上面的代码运行的结果为: I'm proxy! Welcome oschina hosee's blo ...

  6. css flex 属性教程

    https://www.zhangxinxu.com/wordpress/2018/10/display-flex-css3-css/#align-self display: flex; flex-w ...

  7. 百度地图报错:APP Referer校验失败

    今天微信小程序,通过经纬度,调用百度api,将经纬度转换成城市名和街道地址,结果小程序报错. 错误信息如下: 这个是KEY的白名单设置问题.因为白名单设置限制了来源信息.只要在下面红色部分设置IP,或 ...

  8. Mono vs IL2CPP

    [Mono vs IL2CPP]             参考:http://blog.csdn.net/gz_huangzl/article/details/52486255

  9. 循环流程控制&方法(3)

    1.循环流程控制 当某一段代码需要重复执行多次的时候,就需要用到循环: 循环三要素: 循环的起点:循环的终点(结束条件):步长: 当循环条件不再成立,结束循环: for循环 for(循环起点:循环条件 ...

  10. MAT eclipse内存分析工具

      启动的时候提示: Failed to load the JNIshared library 解决办法: 查看配置文件:MemoryAnalyzer.ini --launcher.librarypl ...