STL标准库-Move对容器效率的影响
技术在于交流、沟通,本文为博主原创文章转载请注明出处并保持作品的完整性
C++11新增move()语法(我暂时交错右值引用),在前面我有一篇文章叫 C++11_右值引用 简单的介绍了右值引用类的实现,这节我主要介绍一下为什么move()会更高效.
这次主要以一个带右值引用的Person类,和vector做测试
首先我们先实现一个带右值引用的Person类
- class Person
- {
- public:
- static size_t DCtor; //记录默认构造函数调用次数
- static size_t Ctor; //记录构造函数调用次数
- static size_t CCtor;//记录拷贝函数调用次数
- static size_t CAsgn;//记录赋值拷贝调用次数
- static size_t MCtor;//记录move 构造调用次数
- static size_t MAsgn;//记录move 赋值调用次数
- static size_t Dtor;//记录析构函数调用次数
- private:
- int _age;
- char* _name;
- size_t _len;
- void _test_name(const char *s)
- {
- _name = new char[_len+];
- memcpy(_name, s, _len);
- _name[_len] = '\0';
- }
- public:
- //default ctor
- Person(): _age() , _name(NULL), _len(){ DCtor++;}
- Person(const int age, const char * p) : _age(age), _len(strlen(p)) {
- _test_name(p);
- Ctor++;
- }
- //dctor
- ~Person(){
- if(_name){
- delete _name;
- }
- Dtor++;
- }
- // copy ctor
- Person (const Person& p):_age(p._age),_len(p._len){
- _test_name(p._name);
- CCtor++;
- }
- //copy assignment
- Person & operator=(const Person& p)
- {
- if (this != &p){
- if(_name) delete _name;
- _len = p._len;
- _age = p._age;
- _test_name(p._name);
- }
- else{
- cout<< "self Assignment. Nothing to do." <<endl;
- }
- CAsgn++;
- return *this;
- }
- // move cotr , wihth "noexcept"
- Person(Person&& p) noexcept :_age(p._age) , _name(p._name), _len(p._len){
- MCtor++;
- p._age = ;
- p._name = NULL;//必须为NULL 如果你把这里设为空 那么这个函数走完之后将调用析够函数 因为当前的Person类 和你将要析够的Person的_name指向同一部分 析构部分见析构函数
- }
- // move assignment
- Person& operator=(Person&& p) noexcept {
- if (this != &p)
- {
- if(_name) delete _name;
- _age = p._age;
- _len = p._len;
- _name = p._name;
- p._age = ;
- p._len = ;
- p._name = NULL;
- }
- MAsgn++;
- return *this;
- }
- };
- size_t Person::DCtor = ;
- size_t Person::Ctor = ;
- size_t Person::CCtor = ;
- size_t Person::CAsgn = ;
- size_t Person::MCtor = ;
- size_t Person::MAsgn = ;
- size_t Person::Dtor = ;
我们先看正常的拷贝构造函数
- Person (const Person& p):_age(p._age),_len(p._len){
- _test_name(p._name);
- CCtor++;
- }
它是先申请一段新的内存,然后将传进参数咋赋值给新的内存,型似下图
我们在看move 构造函数
- // move cotr , wihth "noexcept"
- Person(Person&& p) noexcept :_age(p._age) , _name(p._name), _len(p._len){
- MCtor++;
- p._age = ;
- p._name = NULL;//必须为NULL 如果你把这里设为空 那么这个函数走完之后将调用析够函数 因为当前的Person类 和你将要析够的Person的_name指向同一部分 析构部分见析构函数
- }
它值复制了指针,没有在去申请内存,就是我们常说的浅拷贝,它只是将原来指向数据的指针打断,然后将复制的指针指向数据,型似下图
只拷贝指针,当然比拷贝数据要快上很多
现在来验证一下上面的结论
- template<typename M, typename NM>
- void test_moveable(M c1, NM c2, long& value)
- {
- char buf[];
- typedef typename iterator_traits<typename M::iterator>::value_type MyPerson;//萃取出type
- clock_t timeStart = clock();//记录起始时间
- for(long i=; i<value; i++)
- {
- snprintf(buf,,"%d",rand());
- auto ite = c1.end();
- c1.insert(ite,MyPerson(,buf));
- }
- cout << "Move Person" << endl;
- cout << "construction, milli-seconds: "<<(clock()-timeStart) << endl;//验证构造耗时
- cout << "size()= " << c1.size() << endl;//验证测试基数 我这里用三百万做基数
- output_Static_data(*c1.begin());
- cout << "copy, milli-seconds: "<<(clock()-timeStart) << endl;//验证copy耗时
- timeStart = clock();//
- M c11(c1);
- cout << "move copy, milli-seconds: "<<(clock()-timeStart) << endl;//验证move copy函数耗时
- timeStart = clock();
- M c12(std::move(c1));
- cout << "move construction, milli-seconds: "<<(clock()-timeStart) << endl;//验证Move 构造耗时
- timeStart = clock();
- c11.swap(c12);//验证seap耗时
- cout << "swap, milli-seconds: "<<(clock()-timeStart) << endl;
- }
从测试结果中我们可以看出 拷贝构造与move构造的耗时差距是巨大的
我们来看一下C++11 vector中的move()的使用,下面是vector<>中的拷贝构造函数的源码
- /**
- * @brief %Vector copy constructor.
- * @param __x A %vector of identical element and allocator types.
- *
- * The newly-created %vector uses a copy of the allocation
- * object used by @a __x. All the elements of @a __x are copied,
- * but any extra memory in
- * @a __x (for fast expansion) will not be copied.
- */
- vector(const vector& __x)
- : _Base(__x.size(),
- _Alloc_traits::_S_select_on_copy(__x._M_get_Tp_allocator()))
- { this->_M_impl._M_finish =
- std::__uninitialized_copy_a(__x.begin(), __x.end(),
- this->_M_impl._M_start,
- _M_get_Tp_allocator());
- }
这里其实就是一个move的使用,这个拷只拷贝指针的函数(将__x.end()赋值给_M_finish,将__x.begin()赋值给_M_impl._M_start),只复制指针,当然效率会更高
vector中还有一处用到了move(),那就是vector的move 构造函数
- /**
- * @brief %Vector move constructor.
- * @param __x A %vector of identical element and allocator types.
- *
- * The newly-created %vector contains the exact contents of @a __x.
- * The contents of @a __x are a valid, but unspecified %vector.
- */
- vector(vector&& __x) noexcept
- : _Base(std::move(__x)) { }
调用
- _Vector_base(_Vector_base&& __x) noexcept
- : _M_impl(std::move(__x._M_get_Tp_allocator()))
- { this->_M_impl._M_swap_data(__x._M_impl); }
调用
- void _M_swap_data(_Vector_impl& __x) _GLIBCXX_NOEXCEPT
- {
- std::swap(_M_start, __x._M_start);
- std::swap(_M_finish, __x._M_finish);
- std::swap(_M_end_of_storage, __x._M_end_of_storage);
- }
vector的move 构造函数 只是将上面的三个指针做了交换,也同样告诉了我们swap()耗时为什么也是这么短.
总结
move 给我们带来了更高效的语法,但是不要忘了,move的实质是浅拷贝,编程中尤其要注意浅拷贝的使用,因为浅拷贝一旦操作不当,可能造成不可预估的错误(如一个变量被删除两次)
上面介绍move虽然做了特殊处理,但是被move处理后的变量,依然不能再使用.(例:如果你使用了这段代码M c12(std::move(c1)); 那么在这之后一定不要在出现 c1 这个变量)
测试代码如下
- #include <iostream>
- #include <vector>
- #include <string.h>//strlen()
- #include <typeinfo>//typeid().name()
- #include <iterator>
- #include <ctime>
- using namespace std;
- class CNoMovePerson
- {
- public:
- static size_t DCtor;
- static size_t Ctor;
- static size_t CCtor;
- static size_t CAsgn;
- static size_t MCtor;
- static size_t MAsgn;
- static size_t Dtor;
- private:
- int _age;
- char* _name;
- size_t _len;
- void _test_name(const char *s)
- {
- _name = new char[_len+];
- memcpy(_name, s, _len);
- _name[_len] = '\0';
- }
- public:
- //default ctor
- CNoMovePerson(): _age() , _name(NULL), _len(){DCtor++;}
- CNoMovePerson(const int age, const char * p) : _age(age), _len(strlen(p)) {
- _test_name(p);
- Ctor++;
- }
- //dctor
- ~CNoMovePerson(){
- if(_name){
- delete _name;
- }
- Dtor++;
- }
- // copy ctor
- CNoMovePerson (const CNoMovePerson& p):_age(p._age),_len(p._len){
- _test_name(p._name);
- CCtor++;}
- //copy assignment
- CNoMovePerson & operator=(const CNoMovePerson& p)
- {
- if (this != &p){
- if(_name) delete _name;
- _len = p._len;
- _age = p._age;
- _test_name(p._name);
- }
- else{
- cout<< "self Assignment. Nothing to do." <<endl;
- }
- CAsgn++;
- return *this;
- }
- };
- size_t CNoMovePerson::DCtor = ;
- size_t CNoMovePerson::Ctor = ;
- size_t CNoMovePerson::CCtor = ;
- size_t CNoMovePerson::CAsgn = ;
- size_t CNoMovePerson::MCtor = ;
- size_t CNoMovePerson::MAsgn = ;
- size_t CNoMovePerson::Dtor = ;
- class Person
- {
- public:
- static size_t DCtor;
- static size_t Ctor;
- static size_t CCtor;
- static size_t CAsgn;
- static size_t MCtor;
- static size_t MAsgn;
- static size_t Dtor;
- private:
- int _age;
- char* _name;
- size_t _len;
- void _test_name(const char *s)
- {
- _name = new char[_len+];
- memcpy(_name, s, _len);
- _name[_len] = '\0';
- }
- public:
- //default ctor
- Person(): _age() , _name(NULL), _len(){ DCtor++;}
- Person(const int age, const char * p) : _age(age), _len(strlen(p)) {
- _test_name(p);
- Ctor++;
- }
- //dctor
- ~Person(){
- if(_name){
- delete _name;
- }
- Dtor++;
- }
- // copy ctor
- Person (const Person& p):_age(p._age),_len(p._len){
- _test_name(p._name);
- CCtor++;
- }
- //copy assignment
- Person & operator=(const Person& p)
- {
- if (this != &p){
- if(_name) delete _name;
- _len = p._len;
- _age = p._age;
- _test_name(p._name);
- }
- else{
- cout<< "self Assignment. Nothing to do." <<endl;
- }
- CAsgn++;
- return *this;
- }
- // move cotr , wihth "noexcept"
- Person(Person&& p) noexcept :_age(p._age) , _name(p._name), _len(p._len){
- MCtor++;
- p._age = ;
- p._name = NULL;//必须为NULL 如果你把这里设为空 那么这个函数走完之后将调用析够函数 因为当前的Person类 和你将要析够的Person的_name指向同一部分 析构部分见析构函数
- }
- // move assignment
- Person& operator=(Person&& p) noexcept {
- if (this != &p)
- {
- if(_name) delete _name;
- _age = p._age;
- _len = p._len;
- _name = p._name;
- p._age = ;
- p._len = ;
- p._name = NULL;
- }
- MAsgn++;
- return *this;
- }
- };
- size_t Person::DCtor = ;
- size_t Person::Ctor = ;
- size_t Person::CCtor = ;
- size_t Person::CAsgn = ;
- size_t Person::MCtor = ;
- size_t Person::MAsgn = ;
- size_t Person::Dtor = ;
- template<typename T>
- void output_Static_data(const T& myPerson)
- {
- cout << typeid(myPerson).name() << "--" << endl;
- cout << "CCtor=" << T::CCtor <<endl
- << "MCtor=" << T::MCtor <<endl
- << "CAsgn=" << T::CAsgn <<endl
- << "MAsgn=" << T::MAsgn <<endl
- << "Dtor=" << T::Dtor <<endl
- << "Ctor=" << T::Ctor <<endl
- << "DCtor=" << T::DCtor <<endl
- << endl;
- }
- template<typename M, typename NM>
- void test_moveable(M c1, NM c2, long& value)
- {
- char buf[];
- typedef typename iterator_traits<typename M::iterator>::value_type MyPerson;
- clock_t timeStart = clock();
- for(long i=; i<value; i++)
- {
- snprintf(buf,,"%d",rand());
- auto ite = c1.end();
- c1.insert(ite,MyPerson(,buf));
- }
- cout << "Move Person" << endl;
- cout << "construction, milli-seconds: "<<(clock()-timeStart) << endl;
- cout << "size()= " << c1.size() << endl;
- output_Static_data(*c1.begin());
- timeStart = clock();
- M c11(c1);
- cout << "copy, milli-seconds: "<<(clock()-timeStart) << endl;
- timeStart = clock();
- M c12(std::move(c1));
- cout << "move construction, milli-seconds: "<<(clock()-timeStart) << endl;
- timeStart = clock();
- c11.swap(c12);
- cout << "swap, milli-seconds: "<<(clock()-timeStart) << endl;
- cout << "------------------------------" << endl;
- cout << "No Move Person" << endl;
- typedef typename iterator_traits<typename NM::iterator>::value_type MyPersonNoMove;
- timeStart = clock();
- for(long i=; i<value; i++)
- {
- snprintf(buf,,"%d",rand());
- auto ite = c2.end();
- c2.insert(ite,MyPersonNoMove(,buf));
- }
- cout << "construction, milli-seconds: "<<(clock()-timeStart) << endl;
- cout << "size()= " << c2.size() << endl;
- output_Static_data(*c1.begin());
- timeStart = clock();
- NM c22(c2);
- cout << "move copy, milli-seconds: "<<(clock()-timeStart) << endl;
- timeStart = clock();
- NM c222(std::move(c2));
- cout << "move construction, milli-seconds: "<<(clock()-timeStart) << endl;
- timeStart = clock();
- c22.swap(c222);
- cout << "swap, milli-seconds: "<<(clock()-timeStart) << endl;
- }
- long value = ;
- int main()
- {
- test_moveable(vector<Person>(),vector<CNoMovePerson>(),value);
- return ;
- }
参考侯捷<<STL源码剖析>>
STL标准库-Move对容器效率的影响的更多相关文章
- STL标准库中的容器
容器:顾名思义,我的理解就是把同一种数据类型括起来,作为一捆.如vector<int> ,vector就是个容器,里面全是一个个的int型数据. 容器包括三大块: 顺序型容器: (1)ve ...
- STL标准库-容器-set与multiset
技术在于交流.沟通,转载请注明出处并保持作品的完整性. set与multiset关联容器 结构如下 set是一种关联容器,key即value,value即key.它是自动排序,排序特点依据key se ...
- STL标准库-容器-deque
技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. deque双向开口可进可出的容器 我们知道连续内存的容器不能随意扩充,因为这样容易扩充别人那去 deque却可以,它创造了内存 ...
- STL标准库-容器-vector
技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. 向量容器vector是一个动态数组,内存连续,它是动态分配内存,且每次扩张的原来的二倍. 他的结构如下 一 定义 vector ...
- STL标准库-容器-set与map
STL标准库-容器-set与multiset C++的set https://www.cnblogs.com/LearningTheLoad/p/7456024.html STL标准库-容器-map和 ...
- STL标准库-算法-常用算法
技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 介绍11种STL标准库的算法,从这11种算法中总结一下算法的基本使用 1.accumulate() 累加 2.for_each( ...
- C++STL标准库学习笔记(五)set
前言: 在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标记了出来,这一篇后面主要都是我的记录了,为了防止大片蓝色字体出现,后面就不改蓝色 ...
- C++STL标准库学习笔记(三)multiset
C++STL标准库学习笔记(三)multiset STL中的平衡二叉树数据结构 前言: 在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标 ...
- C++STL标准库学习笔记(一)sort
前言: 近来在学习STL标准库,做一份笔记并整理好,方便自己梳理知识.以后查找,也方便他人学习,两全其美,快哉快哉! 这里我会以中国大学慕课上北京大学郭炜老师的<程序设计与算法(一)C语言程序设 ...
随机推荐
- C#学习笔记(七):结构体、数组、冒泡排序和调试
结构体 结构体不能重写默认无参构造函数 一位数组 using System; using System.Collections.Generic; using System.Linq; using Sy ...
- HDU 6070 Dirt Ratio(分数规划+线段树)
http://acm.hdu.edu.cn/showproblem.php?pid=6070 题意: 找出一个区间,使得(区间内不同数的个数/区间长度)的值最小,并输出该值. 思路: 因为是要求$\f ...
- AtCoder square869120 Contest #3 F sushi
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- Could NOT find SDL (missing: SDL_LIBRARY SDL_INCLUDE_DIR)
sudo apt-get install libsdl-dev或 sudo apt-get install libsdl1.2-dev
- js 基础数据类型和引用类型 ,深浅拷贝问题,以及内存分配问题
js 深浅拷贝问题 浅拷贝一般指的是基本类型的复制 深拷贝一般指引用类型的拷贝,把引用类型的值也拷贝出来 举例 h5的sessionStorage只能存放字符串,所以要存储json时就要把json使用 ...
- Chrome与之驱动对应的版本
看到网上基本没有最新的chromedriver与chrome的对应关系表,便兴起整理了一份如下,希望对大家有用: chromedriver版本 支持的Chrome版本 v2.46 v71-73 v2. ...
- HTTP URL 字符转义 字符编码 、 RFC 3986编码规范
一.为什么要编码转义 通常如果一样东西需要编码,说明这样东西并不适合传输.原因多种多样,如Size过大,包含隐私数据,对于Url来说,之所以要进行编码,是因为Url中有些字符会引起歧义. 例如Url参 ...
- PHP函数总结 (二)
<?php header('content-type:text/html;charset=utf8');// 只要声明的函数在脚本中可见,就可以通过函数名在脚本的任何位置调用echo table ...
- 利用nodeJs anywhere搭建本地服务器环境
1.npm install anywhere -g 如果是mac系统会提示你权限不够,需要在代码前加上 sudo获取管理员权限.即sudo npm install anywhere -g. 2.安装完 ...
- JAVA System.arraycopy 和Arrays.copyof 效率比较
System.arraycopy()源码.可以看到是native方法: native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中. ...