技术在于交流、沟通,本文为博主原创文章转载请注明出处并保持作品的完整性

C++11新增move()语法(我暂时交错右值引用),在前面我有一篇文章叫 C++11_右值引用 简单的介绍了右值引用类的实现,这节我主要介绍一下为什么move()会更高效.

这次主要以一个带右值引用的Person类,和vector做测试

首先我们先实现一个带右值引用的Person类

  1. class Person
  2. {
  3.  
  4. public:
  5. static size_t DCtor; //记录默认构造函数调用次数
  6. static size_t Ctor; //记录构造函数调用次数
  7. static size_t CCtor;//记录拷贝函数调用次数
  8. static size_t CAsgn;//记录赋值拷贝调用次数
  9. static size_t MCtor;//记录move 构造调用次数
  10. static size_t MAsgn;//记录move 赋值调用次数
  11. static size_t Dtor;//记录析构函数调用次数
  12.  
  13. private:
  14. int _age;
  15. char* _name;
  16. size_t _len;
  17. void _test_name(const char *s)
  18. {
  19. _name = new char[_len+];
  20. memcpy(_name, s, _len);
  21. _name[_len] = '\0';
  22. }
  23.  
  24. public:
  25. //default ctor
  26. Person(): _age() , _name(NULL), _len(){ DCtor++;}
  27.  
  28. Person(const int age, const char * p) : _age(age), _len(strlen(p)) {
  29. _test_name(p);
  30. Ctor++;
  31. }
  32.  
  33. //dctor
  34. ~Person(){
  35. if(_name){
  36. delete _name;
  37. }
  38. Dtor++;
  39. }
  40.  
  41. // copy ctor
  42. Person (const Person& p):_age(p._age),_len(p._len){
  43. _test_name(p._name);
  44. CCtor++;
  45. }
  46.  
  47. //copy assignment
  48. Person & operator=(const Person& p)
  49. {
  50. if (this != &p){
  51. if(_name) delete _name;
  52. _len = p._len;
  53. _age = p._age;
  54. _test_name(p._name);
  55. }
  56. else{
  57. cout<< "self Assignment. Nothing to do." <<endl;
  58. }
  59. CAsgn++;
  60. return *this;
  61. }
  62.  
  63. // move cotr , wihth "noexcept"
  64. Person(Person&& p) noexcept :_age(p._age) , _name(p._name), _len(p._len){
  65. MCtor++;
  66. p._age = ;
  67. p._name = NULL;//必须为NULL 如果你把这里设为空 那么这个函数走完之后将调用析够函数 因为当前的Person类 和你将要析够的Person的_name指向同一部分 析构部分见析构函数
  68. }
  69. // move assignment
  70. Person& operator=(Person&& p) noexcept {
  71.  
  72. if (this != &p)
  73. {
  74. if(_name) delete _name;
  75. _age = p._age;
  76. _len = p._len;
  77. _name = p._name;
  78. p._age = ;
  79. p._len = ;
  80. p._name = NULL;
  81. }
  82. MAsgn++;
  83. return *this;
  84. }
  85. };
  86.  
  87. size_t Person::DCtor = ;
  88. size_t Person::Ctor = ;
  89. size_t Person::CCtor = ;
  90. size_t Person::CAsgn = ;
  91. size_t Person::MCtor = ;
  92. size_t Person::MAsgn = ;
  93. size_t Person::Dtor = ;

我们先看正常的拷贝构造函数

  1. Person (const Person& p):_age(p._age),_len(p._len){
  2. _test_name(p._name);
  3. CCtor++;
  4. }

它是先申请一段新的内存,然后将传进参数咋赋值给新的内存,型似下图

我们在看move 构造函数

  1. // move cotr , wihth "noexcept"
  2. Person(Person&& p) noexcept :_age(p._age) , _name(p._name), _len(p._len){
  3. MCtor++;
  4. p._age = ;
  5. p._name = NULL;//必须为NULL 如果你把这里设为空 那么这个函数走完之后将调用析够函数 因为当前的Person类 和你将要析够的Person的_name指向同一部分 析构部分见析构函数
  6. }

它值复制了指针,没有在去申请内存,就是我们常说的浅拷贝,它只是将原来指向数据的指针打断,然后将复制的指针指向数据,型似下图

只拷贝指针,当然比拷贝数据要快上很多


现在来验证一下上面的结论

  1. template<typename M, typename NM>
  2. void test_moveable(M c1, NM c2, long& value)
  3. {
  4. char buf[];
  5.  
  6. typedef typename iterator_traits<typename M::iterator>::value_type MyPerson;//萃取出type
  7.  
  8. clock_t timeStart = clock();//记录起始时间
  9. for(long i=; i<value; i++)
  10. {
  11. snprintf(buf,,"%d",rand());
  12. auto ite = c1.end();
  13. c1.insert(ite,MyPerson(,buf));
  14. }
  15. cout << "Move Person" << endl;
  16. cout << "construction, milli-seconds: "<<(clock()-timeStart) << endl;//验证构造耗时
  17. cout << "size()= " << c1.size() << endl;//验证测试基数 我这里用三百万做基数
  18.  
  19. output_Static_data(*c1.begin());
  20. cout << "copy, milli-seconds: "<<(clock()-timeStart) << endl;//验证copy耗时
  21.  
  22. timeStart = clock();//
  23. M c11(c1);
  24. cout << "move copy, milli-seconds: "<<(clock()-timeStart) << endl;//验证move copy函数耗时
  25.  
  26. timeStart = clock();
  27. M c12(std::move(c1));
  28. cout << "move construction, milli-seconds: "<<(clock()-timeStart) << endl;//验证Move 构造耗时
  29.  
  30. timeStart = clock();
  31. c11.swap(c12);//验证seap耗时
  32. cout << "swap, milli-seconds: "<<(clock()-timeStart) << endl;
  33. }

从测试结果中我们可以看出 拷贝构造与move构造的耗时差距是巨大的


我们来看一下C++11 vector中的move()的使用,下面是vector<>中的拷贝构造函数的源码

  1. /**
  2. * @brief %Vector copy constructor.
  3. * @param __x A %vector of identical element and allocator types.
  4. *
  5. * The newly-created %vector uses a copy of the allocation
  6. * object used by @a __x. All the elements of @a __x are copied,
  7. * but any extra memory in
  8. * @a __x (for fast expansion) will not be copied.
  9. */
  10. vector(const vector& __x)
  11. : _Base(__x.size(),
  12. _Alloc_traits::_S_select_on_copy(__x._M_get_Tp_allocator()))
  13. { this->_M_impl._M_finish =
  14. std::__uninitialized_copy_a(__x.begin(), __x.end(),
  15. this->_M_impl._M_start,
  16. _M_get_Tp_allocator());
  17. }

这里其实就是一个move的使用,这个拷只拷贝指针的函数(将__x.end()赋值给_M_finish,将__x.begin()赋值给_M_impl._M_start),只复制指针,当然效率会更高


vector中还有一处用到了move(),那就是vector的move 构造函数

  1. /**
  2. * @brief %Vector move constructor.
  3. * @param __x A %vector of identical element and allocator types.
  4. *
  5. * The newly-created %vector contains the exact contents of @a __x.
  6. * The contents of @a __x are a valid, but unspecified %vector.
  7. */
  8. vector(vector&& __x) noexcept
  9. : _Base(std::move(__x)) { }

调用

  1. _Vector_base(_Vector_base&& __x) noexcept
  2. : _M_impl(std::move(__x._M_get_Tp_allocator()))
  3. { this->_M_impl._M_swap_data(__x._M_impl); }

调用

  1. void _M_swap_data(_Vector_impl& __x) _GLIBCXX_NOEXCEPT
  2. {
  3. std::swap(_M_start, __x._M_start);
  4. std::swap(_M_finish, __x._M_finish);
  5. std::swap(_M_end_of_storage, __x._M_end_of_storage);
  6. }

vector的move 构造函数 只是将上面的三个指针做了交换,也同样告诉了我们swap()耗时为什么也是这么短.

总结

move 给我们带来了更高效的语法,但是不要忘了,move的实质是浅拷贝,编程中尤其要注意浅拷贝的使用,因为浅拷贝一旦操作不当,可能造成不可预估的错误(如一个变量被删除两次)

上面介绍move虽然做了特殊处理,但是被move处理后的变量,依然不能再使用.(例:如果你使用了这段代码M c12(std::move(c1)); 那么在这之后一定不要在出现 c1 这个变量)

测试代码如下

  1. #include <iostream>
  2. #include <vector>
  3. #include <string.h>//strlen()
  4. #include <typeinfo>//typeid().name()
  5. #include <iterator>
  6. #include <ctime>
  7.  
  8. using namespace std;
  9.  
  10. class CNoMovePerson
  11. {
  12. public:
  13. static size_t DCtor;
  14. static size_t Ctor;
  15. static size_t CCtor;
  16. static size_t CAsgn;
  17. static size_t MCtor;
  18. static size_t MAsgn;
  19. static size_t Dtor;
  20. private:
  21. int _age;
  22. char* _name;
  23. size_t _len;
  24. void _test_name(const char *s)
  25. {
  26. _name = new char[_len+];
  27. memcpy(_name, s, _len);
  28. _name[_len] = '\0';
  29. }
  30.  
  31. public:
  32. //default ctor
  33. CNoMovePerson(): _age() , _name(NULL), _len(){DCtor++;}
  34.  
  35. CNoMovePerson(const int age, const char * p) : _age(age), _len(strlen(p)) {
  36. _test_name(p);
  37. Ctor++;
  38. }
  39.  
  40. //dctor
  41. ~CNoMovePerson(){
  42. if(_name){
  43. delete _name;
  44. }
  45. Dtor++;
  46. }
  47.  
  48. // copy ctor
  49. CNoMovePerson (const CNoMovePerson& p):_age(p._age),_len(p._len){
  50. _test_name(p._name);
  51. CCtor++;}
  52.  
  53. //copy assignment
  54. CNoMovePerson & operator=(const CNoMovePerson& p)
  55. {
  56. if (this != &p){
  57. if(_name) delete _name;
  58. _len = p._len;
  59. _age = p._age;
  60. _test_name(p._name);
  61. }
  62. else{
  63. cout<< "self Assignment. Nothing to do." <<endl;
  64. }
  65. CAsgn++;
  66. return *this;
  67. }
  68. };
  69.  
  70. size_t CNoMovePerson::DCtor = ;
  71. size_t CNoMovePerson::Ctor = ;
  72. size_t CNoMovePerson::CCtor = ;
  73. size_t CNoMovePerson::CAsgn = ;
  74. size_t CNoMovePerson::MCtor = ;
  75. size_t CNoMovePerson::MAsgn = ;
  76. size_t CNoMovePerson::Dtor = ;
  77.  
  78. class Person
  79. {
  80.  
  81. public:
  82. static size_t DCtor;
  83. static size_t Ctor;
  84. static size_t CCtor;
  85. static size_t CAsgn;
  86. static size_t MCtor;
  87. static size_t MAsgn;
  88. static size_t Dtor;
  89.  
  90. private:
  91. int _age;
  92. char* _name;
  93. size_t _len;
  94. void _test_name(const char *s)
  95. {
  96. _name = new char[_len+];
  97. memcpy(_name, s, _len);
  98. _name[_len] = '\0';
  99. }
  100.  
  101. public:
  102. //default ctor
  103. Person(): _age() , _name(NULL), _len(){ DCtor++;}
  104.  
  105. Person(const int age, const char * p) : _age(age), _len(strlen(p)) {
  106. _test_name(p);
  107. Ctor++;
  108. }
  109.  
  110. //dctor
  111. ~Person(){
  112. if(_name){
  113. delete _name;
  114. }
  115. Dtor++;
  116. }
  117.  
  118. // copy ctor
  119. Person (const Person& p):_age(p._age),_len(p._len){
  120. _test_name(p._name);
  121. CCtor++;
  122. }
  123.  
  124. //copy assignment
  125. Person & operator=(const Person& p)
  126. {
  127. if (this != &p){
  128. if(_name) delete _name;
  129. _len = p._len;
  130. _age = p._age;
  131. _test_name(p._name);
  132. }
  133. else{
  134. cout<< "self Assignment. Nothing to do." <<endl;
  135. }
  136. CAsgn++;
  137. return *this;
  138. }
  139.  
  140. // move cotr , wihth "noexcept"
  141. Person(Person&& p) noexcept :_age(p._age) , _name(p._name), _len(p._len){
  142. MCtor++;
  143. p._age = ;
  144. p._name = NULL;//必须为NULL 如果你把这里设为空 那么这个函数走完之后将调用析够函数 因为当前的Person类 和你将要析够的Person的_name指向同一部分 析构部分见析构函数
  145. }
  146. // move assignment
  147. Person& operator=(Person&& p) noexcept {
  148.  
  149. if (this != &p)
  150. {
  151. if(_name) delete _name;
  152. _age = p._age;
  153. _len = p._len;
  154. _name = p._name;
  155. p._age = ;
  156. p._len = ;
  157. p._name = NULL;
  158. }
  159. MAsgn++;
  160. return *this;
  161. }
  162. };
  163.  
  164. size_t Person::DCtor = ;
  165. size_t Person::Ctor = ;
  166. size_t Person::CCtor = ;
  167. size_t Person::CAsgn = ;
  168. size_t Person::MCtor = ;
  169. size_t Person::MAsgn = ;
  170. size_t Person::Dtor = ;
  171.  
  172. template<typename T>
  173. void output_Static_data(const T& myPerson)
  174. {
  175. cout << typeid(myPerson).name() << "--" << endl;
  176. cout << "CCtor=" << T::CCtor <<endl
  177. << "MCtor=" << T::MCtor <<endl
  178. << "CAsgn=" << T::CAsgn <<endl
  179. << "MAsgn=" << T::MAsgn <<endl
  180. << "Dtor=" << T::Dtor <<endl
  181. << "Ctor=" << T::Ctor <<endl
  182. << "DCtor=" << T::DCtor <<endl
  183. << endl;
  184. }
  185.  
  186. template<typename M, typename NM>
  187. void test_moveable(M c1, NM c2, long& value)
  188. {
  189. char buf[];
  190.  
  191. typedef typename iterator_traits<typename M::iterator>::value_type MyPerson;
  192.  
  193. clock_t timeStart = clock();
  194. for(long i=; i<value; i++)
  195. {
  196. snprintf(buf,,"%d",rand());
  197. auto ite = c1.end();
  198. c1.insert(ite,MyPerson(,buf));
  199. }
  200. cout << "Move Person" << endl;
  201. cout << "construction, milli-seconds: "<<(clock()-timeStart) << endl;
  202. cout << "size()= " << c1.size() << endl;
  203.  
  204. output_Static_data(*c1.begin());
  205.  
  206. timeStart = clock();
  207. M c11(c1);
  208. cout << "copy, milli-seconds: "<<(clock()-timeStart) << endl;
  209.  
  210. timeStart = clock();
  211. M c12(std::move(c1));
  212. cout << "move construction, milli-seconds: "<<(clock()-timeStart) << endl;
  213.  
  214. timeStart = clock();
  215. c11.swap(c12);
  216. cout << "swap, milli-seconds: "<<(clock()-timeStart) << endl;
  217.  
  218. cout << "------------------------------" << endl;
  219. cout << "No Move Person" << endl;
  220.  
  221. typedef typename iterator_traits<typename NM::iterator>::value_type MyPersonNoMove;
  222.  
  223. timeStart = clock();
  224. for(long i=; i<value; i++)
  225. {
  226. snprintf(buf,,"%d",rand());
  227. auto ite = c2.end();
  228. c2.insert(ite,MyPersonNoMove(,buf));
  229. }
  230.  
  231. cout << "construction, milli-seconds: "<<(clock()-timeStart) << endl;
  232. cout << "size()= " << c2.size() << endl;
  233.  
  234. output_Static_data(*c1.begin());
  235.  
  236. timeStart = clock();
  237. NM c22(c2);
  238. cout << "move copy, milli-seconds: "<<(clock()-timeStart) << endl;
  239.  
  240. timeStart = clock();
  241. NM c222(std::move(c2));
  242. cout << "move construction, milli-seconds: "<<(clock()-timeStart) << endl;
  243.  
  244. timeStart = clock();
  245. c22.swap(c222);
  246. cout << "swap, milli-seconds: "<<(clock()-timeStart) << endl;
  247. }
  248.  
  249. long value = ;
  250. int main()
  251. {
  252. test_moveable(vector<Person>(),vector<CNoMovePerson>(),value);
  253. return ;
  254. }

参考侯捷<<STL源码剖析>>

STL标准库-Move对容器效率的影响的更多相关文章

  1. STL标准库中的容器

    容器:顾名思义,我的理解就是把同一种数据类型括起来,作为一捆.如vector<int> ,vector就是个容器,里面全是一个个的int型数据. 容器包括三大块: 顺序型容器: (1)ve ...

  2. STL标准库-容器-set与multiset

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. set与multiset关联容器 结构如下 set是一种关联容器,key即value,value即key.它是自动排序,排序特点依据key se ...

  3. STL标准库-容器-deque

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. deque双向开口可进可出的容器 我们知道连续内存的容器不能随意扩充,因为这样容易扩充别人那去 deque却可以,它创造了内存 ...

  4. STL标准库-容器-vector

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. 向量容器vector是一个动态数组,内存连续,它是动态分配内存,且每次扩张的原来的二倍. 他的结构如下 一 定义 vector ...

  5. STL标准库-容器-set与map

    STL标准库-容器-set与multiset C++的set https://www.cnblogs.com/LearningTheLoad/p/7456024.html STL标准库-容器-map和 ...

  6. STL标准库-算法-常用算法

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 介绍11种STL标准库的算法,从这11种算法中总结一下算法的基本使用 1.accumulate() 累加 2.for_each( ...

  7. C++STL标准库学习笔记(五)set

    前言: 在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标记了出来,这一篇后面主要都是我的记录了,为了防止大片蓝色字体出现,后面就不改蓝色 ...

  8. C++STL标准库学习笔记(三)multiset

    C++STL标准库学习笔记(三)multiset STL中的平衡二叉树数据结构 前言: 在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标 ...

  9. C++STL标准库学习笔记(一)sort

    前言: 近来在学习STL标准库,做一份笔记并整理好,方便自己梳理知识.以后查找,也方便他人学习,两全其美,快哉快哉! 这里我会以中国大学慕课上北京大学郭炜老师的<程序设计与算法(一)C语言程序设 ...

随机推荐

  1. C#学习笔记(七):结构体、数组、冒泡排序和调试

    结构体 结构体不能重写默认无参构造函数 一位数组 using System; using System.Collections.Generic; using System.Linq; using Sy ...

  2. HDU 6070 Dirt Ratio(分数规划+线段树)

    http://acm.hdu.edu.cn/showproblem.php?pid=6070 题意: 找出一个区间,使得(区间内不同数的个数/区间长度)的值最小,并输出该值. 思路: 因为是要求$\f ...

  3. AtCoder square869120 Contest #3 F sushi

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  4. Could NOT find SDL (missing: SDL_LIBRARY SDL_INCLUDE_DIR)

    sudo apt-get install libsdl-dev或 sudo apt-get install libsdl1.2-dev

  5. js 基础数据类型和引用类型 ,深浅拷贝问题,以及内存分配问题

    js 深浅拷贝问题 浅拷贝一般指的是基本类型的复制 深拷贝一般指引用类型的拷贝,把引用类型的值也拷贝出来 举例 h5的sessionStorage只能存放字符串,所以要存储json时就要把json使用 ...

  6. Chrome与之驱动对应的版本

    看到网上基本没有最新的chromedriver与chrome的对应关系表,便兴起整理了一份如下,希望对大家有用: chromedriver版本 支持的Chrome版本 v2.46 v71-73 v2. ...

  7. HTTP URL 字符转义 字符编码 、 RFC 3986编码规范

    一.为什么要编码转义 通常如果一样东西需要编码,说明这样东西并不适合传输.原因多种多样,如Size过大,包含隐私数据,对于Url来说,之所以要进行编码,是因为Url中有些字符会引起歧义. 例如Url参 ...

  8. PHP函数总结 (二)

    <?php header('content-type:text/html;charset=utf8');// 只要声明的函数在脚本中可见,就可以通过函数名在脚本的任何位置调用echo table ...

  9. 利用nodeJs anywhere搭建本地服务器环境

    1.npm install anywhere -g 如果是mac系统会提示你权限不够,需要在代码前加上 sudo获取管理员权限.即sudo npm install anywhere -g. 2.安装完 ...

  10. JAVA System.arraycopy 和Arrays.copyof 效率比较

    System.arraycopy()源码.可以看到是native方法: native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中. ...