转https://blog.csdn.net/wangshubo1989/article/details/49748703

按值传递的意义是什么? 
当一个函数的参数按值传递时,这就会进行拷贝。当然,编译器懂得如何去拷贝。 
而对于我们自定义的类型,我们也许需要提供拷贝构造函数。

但是不得不说,拷贝的代价是昂贵的。

所以我们需要寻找一个避免不必要拷贝的方法,即C++11提供的移动语义。 
上一篇博客中有一个句话用到了:

#include <iostream>void f(int& i) { std::cout << "lvalue ref: " << i << "\n"; }
void f(int&& i) { std::cout << "rvalue ref: " << i << "\n"; }int main()
{int i = 77;f(i);    // lvalue ref calledf(99);   // rvalue ref calledf(std::move(i));  // 稍后介绍return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

实际上,右值引用注意用于创建移动构造函数和移动赋值运算。

移动构造函数类似于拷贝构造函数,把类的实例对象作为参数,并创建一个新的实例对象。 
但是 移动构造函数可以避免内存的重新分配,因为我们知道右值引用提供了一个暂时的对象,而不是进行copy,所以我们可以进行移动。

换言之,在设计到关于临时对象时,右值引用和移动语义允许我们避免不必要的拷贝。我们不想拷贝将要消失的临时对象,所以这个临时对象的资源可以被我们用作于其他的对象。

右值就是典型的临时变量,并且他们可以被修改。如果我们知道一个函数的参数是一个右值,我们可以把它当做一个临时存储。这就意味着我们要移动而不是拷贝右值参数的内容。这就会节省很多的空间。

说多无语,看代码:

#include <iostream>
#include <algorithm>class A
{
public:// Simple constructor that initializes the resource.explicit A(size_t length): mLength(length), mData(new int[length]){std::cout << "A(size_t). length = "<< mLength << "." << std::endl;}// Destructor.~A(){std::cout << "~A(). length = " << mLength << ".";if (mData != NULL) {std::cout << " Deleting resource.";delete[] mData;  // Delete the resource.}std::cout << std::endl;}// Copy constructor.A(const A& other): mLength(other.mLength), mData(new int[other.mLength]){std::cout << "A(const A&). length = "<< other.mLength << ". Copying resource." << std::endl;std::copy(other.mData, other.mData + mLength, mData);}// Copy assignment operator.A& operator=(const A& other){std::cout << "operator=(const A&). length = "<< other.mLength << ". Copying resource." << std::endl;if (this != &other) {delete[] mData;  // Free the existing resource.mLength = other.mLength;mData = new int[mLength];std::copy(other.mData, other.mData + mLength, mData);}return *this;}// Move constructor.A(A&& other) : mData(NULL), mLength(0){std::cout << "A(A&&). length = " << other.mLength << ". Moving resource.\n";// Copy the data pointer and its length from the // source object.mData = other.mData;mLength = other.mLength;// Release the data pointer from the source object so that// the destructor does not free the memory multiple times.other.mData = NULL;other.mLength = 0;}// Move assignment operator.A& operator=(A&& other){std::cout << "operator=(A&&). length = " << other.mLength << "." << std::endl;if (this != &other) {// Free the existing resource.delete[] mData;// Copy the data pointer and its length from the // source object.mData = other.mData;mLength = other.mLength;// Release the data pointer from the source object so that// the destructor does not free the memory multiple times.other.mData = NULL;other.mLength = 0;}return *this;}// Retrieves the length of the data resource.size_t Length() const{return mLength;}private:size_t mLength; // The length of the resource.int* mData;     // The resource.
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

移动构造函数 
语法:

A(A&& other) noexcept    // C++11 - specifying non-exception throwing functions
{mData =  other.mData;  // shallow copy or referential copyother.mData = nullptr;
}
  • 1
  • 2
  • 3
  • 4
  • 5

最主要的是没有用到新的资源,是移动而不是拷贝。 
假设一个地址指向了一个有一百万个int元素的数组,使用move构造函数,我们没有创造什么,所以代价很低。

// Move constructor.
A(A&& other) : mData(NULL), mLength(0)
{// Copy the data pointer and its length from the // source object.mData = other.mData;mLength = other.mLength;// Release the data pointer from the source object so that// the destructor does not free the memory multiple times.other.mData = NULL;other.mLength = 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

移动比拷贝更快!!!

移动赋值运算符 
语法:

A& operator=(A&& other) noexcept
{mData =  other.mData;other.mData = nullptr;return *this;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

工作流程这样的:Google上这么说的:

Release any resources that *this currently owns. 
Pilfer other’s resource. 
Set other to a default state. 
Return *this.

// Move assignment operator.
A& operator=(A&& other)
{std::cout << "operator=(A&&). length = " << other.mLength << "." << std::endl;if (this != &other) {// Free the existing resource.delete[] mData;// Copy the data pointer and its length from the // source object.mData = other.mData;mLength = other.mLength;// Release the data pointer from the source object so that// the destructor does not free the memory multiple times.other.mData = NULL;other.mLength = 0;}return *this;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

让我们看几个move带来的好处吧! 
vector众所周知,C++11后对vector也进行了一些优化。例如vector::push_back()被定义为了两种版本的重载,一个是cosnt T&左值作为参数,一个是T&&右值作为参数。例如下面的代码:

std::vector<A> v;
v.push_back(A(25));
v.push_back(A(75));
  • 1
  • 2
  • 3

上面两个push_back()都会调用push_back(T&&)版本,因为他们的参数为右值。这样提高了效率。

而 当参数为左值的时候,会调用push_back(const T&) 。

#include <vector>int main()
{std::vector<A> v;A aObj(25);       // lvaluev.push_back(aObj);  // push_back(const T&)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

但事实我们可以使用 static_cast进行强制:

// calls push_back(T&&)
v.push_back(static_cast<A&&>(aObj));
  • 1
  • 2

我们可以使用std::move完成上面的任务:

v.push_back(std::move(aObj));  //calls push_back(T&&)
  • 1

似乎push_back(T&&)永远是最佳选择,但是一定要记住: 
push_back(T&&) 使得参数为空。如果我们想要保留参数的值,我们这个时候需要使用拷贝,而不是移动。

最后写一个例子,看看如何使用move来交换两个对象:

#include <iostream>
using namespace std;class A
{public:// constructorexplicit A(size_t length): mLength(length), mData(new int[length]) {}// move constructorA(A&& other){mData = other.mData;mLength = other.mLength;other.mData = nullptr;other.mLength = 0;}// move assignmentA& operator=(A&& other) noexcept{mData =  other.mData;mLength = other.mLength;other.mData = nullptr;other.mLength = 0;return *this;}size_t getLength() { return mLength; }void swap(A& other){A temp = move(other);other = move(*this);*this = move(temp);}int* get_mData() { return mData; }private:int *mData;size_t mLength;
};int main()
{A a(11), b(22);cout << a.getLength() << ' ' << b.getLength() << endl;cout << a.get_mData() << ' ' << b.get_mData() << endl;swap(a,b);cout << a.getLength() << ' ' << b.getLength() << endl;cout << a.get_mData() << ' ' << b.get_mData() << endl;return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

转载于:https://www.cnblogs.com/wangshaowei/p/8880394.html

C++11之 Move semantics(移动语义)(转)相关推荐

  1. c++11 移动语义move semantics

    performance, expensive object copies move semantics, temporary objects implemented with rvalue refer ...

  2. Move Semantics and Perfect Forwarding in C++11

    原文链接:Move Semantics and Perfect Forwarding in C++11 介绍 在本文中,我将探讨C++11的move相关的功能特性,重点是如何编写移动构造函数和移动赋值 ...

  3. HALCON 21.11:深度学习笔记---语义分割/边缘提取(12)

    HALCON 21.11:深度学习笔记---语义分割/边缘提取(12) HALCON 21.11.0.0中,实现了深度学习方法. 本章介绍了如何使用基于深度学习的语义分割,包括训练和推理阶段. 通过语 ...

  4. C++11:move移动语义

    前言 我们知道移动语义是通过右值引用来匹配临时值,那么,普通的左值是否也能借助移动语义来优化性能呢,C++11为了解决这个问题,提供了std::move方法来将左值转换成右值. 正文 move是将对象 ...

  5. C++ 右值引用 | 左值、右值、move、移动语义、引用限定符

    文章目录 C++11为什么引入右值? 区分左值引用.右值引用 move 移动语义 移动构造函数 移动赋值运算符 合成的移动操作 小结 引用限定符 规定this是左值or右值 引用限定符与重载 C++1 ...

  6. 重构改善既有代码设计--重构手法11:Move Field (搬移字段)

    你的程序中,某个字段被其所驻类之外的另一个类更多的用到.在目标类建立一个新字段,修改源字段的所有用户,令它们改用新字段.        动机:在类之间移动状态和行为,是重构过程中必不可少的措施.随着系 ...

  7. c++11特性move和forward区别

    1:move属于强转,左值变右值 2:forward左值变左值,右值变右值(不是强转)

  8. C++11(及现代C++风格)和快速迭代式开发

    过去的一年我在微软亚洲研究院做输入法,我们的产品叫"英库拼音输入法" (下载Beta版),如果你用过"英库词典"(现已更名为必应词典),应该知道"英库 ...

  9. CRC校验,用于大家参考和日后查阅,内容引用github,非本人创作。

    C语言 /************************************************************************ Filename: crc.h* * Des ...

  10. 现代C++风格以及在实际开发中的使用——谈微软英库中文输入法开发经验

    本文转载自:http://blog.sina.com.cn/s/blog_4caedc7a0102eg2w.html,各位看客有问题请移步咨询. 编者按:微软英库拼音输入法测试版已正式上线一周多了,我 ...

最新文章

  1. 在阿里云ECS服务器上面开启tomcat服务并且正常后,无法访问怎么办?原来是没有开通外网访问的端口
  2. 推荐8个冷门但硬核的软件和网站,你一定不能错过!
  3. float 属性详解
  4. html5实践开发教程,HTML5基础与实践教程
  5. 1 字节的 utf-8 序列的字节 1 无效_记住:永远不要在MySQL中使用UTF8
  6. 作研究需要的绣花功夫
  7. 新华字典java_新华字典查询示例代码
  8. LeetCode 156. 上下翻转二叉树(DFS)*
  9. 为什么你一直在写bug?原因找到了
  10. 【Unity开源项目精选】AirSim
  11. windows下Eclipse调试ffmpeg
  12. 服务化治理脚本:show-busiest-java-threads。
  13. Java 确定线程池中工作线程数的大小
  14. DBMS_SQL的使用
  15. 【ModuleNotFoundError 与 ImportError】之 most likely due to a circular import
  16. c语言输出字母是问号,为什么数组输出会多一个问号
  17. 这次,AMD又将数据中心标准提高了一大截
  18. 内容为王时代的 “内容策略师”职位描述、角色和职责
  19. POJ - 3179 Corral the Cows【离散化】【前缀和】
  20. 数梦工场,新型互联网领域“独角兽”出笼

热门文章

  1. 大数据之多数据源综合管理系统:数据源配置管理
  2. poj_2286 线段树
  3. iOS6与iOS7屏幕适配技巧
  4. OGRE学习笔记(一)通过例子了解场景管理器---------地形创建
  5. AudioSwitcher for mac(音频控制工具)v3.08 版本支持M1芯片
  6. Android自定义控件之实现listview滑动时渐隐渐现顶部栏
  7. SVN的学习和使用(七)——SVN提交操作
  8. Android开发技巧:给Button的点击上色
  9. AC日记——单词替换 1.7 21
  10. 思维改变生活:不需要经历也能明白