C++11之 Move semantics(移动语义)(转)
转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(移动语义)(转)相关推荐
- c++11 移动语义move semantics
performance, expensive object copies move semantics, temporary objects implemented with rvalue refer ...
- Move Semantics and Perfect Forwarding in C++11
原文链接:Move Semantics and Perfect Forwarding in C++11 介绍 在本文中,我将探讨C++11的move相关的功能特性,重点是如何编写移动构造函数和移动赋值 ...
- HALCON 21.11:深度学习笔记---语义分割/边缘提取(12)
HALCON 21.11:深度学习笔记---语义分割/边缘提取(12) HALCON 21.11.0.0中,实现了深度学习方法. 本章介绍了如何使用基于深度学习的语义分割,包括训练和推理阶段. 通过语 ...
- C++11:move移动语义
前言 我们知道移动语义是通过右值引用来匹配临时值,那么,普通的左值是否也能借助移动语义来优化性能呢,C++11为了解决这个问题,提供了std::move方法来将左值转换成右值. 正文 move是将对象 ...
- C++ 右值引用 | 左值、右值、move、移动语义、引用限定符
文章目录 C++11为什么引入右值? 区分左值引用.右值引用 move 移动语义 移动构造函数 移动赋值运算符 合成的移动操作 小结 引用限定符 规定this是左值or右值 引用限定符与重载 C++1 ...
- 重构改善既有代码设计--重构手法11:Move Field (搬移字段)
你的程序中,某个字段被其所驻类之外的另一个类更多的用到.在目标类建立一个新字段,修改源字段的所有用户,令它们改用新字段. 动机:在类之间移动状态和行为,是重构过程中必不可少的措施.随着系 ...
- c++11特性move和forward区别
1:move属于强转,左值变右值 2:forward左值变左值,右值变右值(不是强转)
- C++11(及现代C++风格)和快速迭代式开发
过去的一年我在微软亚洲研究院做输入法,我们的产品叫"英库拼音输入法" (下载Beta版),如果你用过"英库词典"(现已更名为必应词典),应该知道"英库 ...
- CRC校验,用于大家参考和日后查阅,内容引用github,非本人创作。
C语言 /************************************************************************ Filename: crc.h* * Des ...
- 现代C++风格以及在实际开发中的使用——谈微软英库中文输入法开发经验
本文转载自:http://blog.sina.com.cn/s/blog_4caedc7a0102eg2w.html,各位看客有问题请移步咨询. 编者按:微软英库拼音输入法测试版已正式上线一周多了,我 ...
最新文章
- 在阿里云ECS服务器上面开启tomcat服务并且正常后,无法访问怎么办?原来是没有开通外网访问的端口
- 推荐8个冷门但硬核的软件和网站,你一定不能错过!
- float 属性详解
- html5实践开发教程,HTML5基础与实践教程
- 1 字节的 utf-8 序列的字节 1 无效_记住:永远不要在MySQL中使用UTF8
- 作研究需要的绣花功夫
- 新华字典java_新华字典查询示例代码
- LeetCode 156. 上下翻转二叉树(DFS)*
- 为什么你一直在写bug?原因找到了
- 【Unity开源项目精选】AirSim
- windows下Eclipse调试ffmpeg
- 服务化治理脚本:show-busiest-java-threads。
- Java 确定线程池中工作线程数的大小
- DBMS_SQL的使用
- 【ModuleNotFoundError 与 ImportError】之 most likely due to a circular import
- c语言输出字母是问号,为什么数组输出会多一个问号
- 这次,AMD又将数据中心标准提高了一大截
- 内容为王时代的 “内容策略师”职位描述、角色和职责
- POJ - 3179 Corral the Cows【离散化】【前缀和】
- 数梦工场,新型互联网领域“独角兽”出笼