vector其中一个特点:内存空间只会增长,不会减小

援引C++ Primer:为了支持快速的随机访问,vector容器的元素以连续方式存放,每一个元素都紧挨着前一个元素存储。

设想一下,当vector添加一个元素时,为了满足连续存放这个特性,都需要重新分配空间、拷贝元素、撤销旧空间,这样性能难以接受。因此STL实现者在对vector进行内存分配时,其实际分配的容量要比当前所需的空间多一些。就是说,vector容器预留了一些额外的存储区,用于存放新添加的元素,这样就不必为每个新元素重新分配整个容器的内存空间。

原文:https://www.cnblogs.com/biyeymyhjob/archive/2012/09/12/2674004.html


push_back复杂度O(1),插入最坏情况的复杂度为O(n)。

不需要在其他位置插入元素,需要随机访问,用vector是比较不错的。

要是有时候需要中间和其他位置的插入和删除,那么list可能是个不错的选择。


push_back发生了什么

#include <iostream>
#include <vector>class Point
{public:Point(){std::cout<<"construction is called"<<std::endl;}Point(const Point& p){std::cout<<"copy construction is called"<<std::endl;}~Point(){std::cout<<"destruction is called"<<std::endl;}
};int main()
{std::vector<Point> pointVec;Point a;Point b;pointVec.push_back(a);pointVec.push_back(b);std::cout<<pointVec.size()<<std::endl;return 0;
}

其中执行

pointVec.push_back(a);

此时vector会申请一个内存空间,并调用拷贝构造函数将a放到vector中

再调用

pointVec.push_back(b);

此时内存不够 需要扩大内存,重新分配内存 这时再调用拷贝构造函数将a拷贝到新的内存,再将b拷入新的内存(所以拷贝构造函数调用了三次),紧接着将原来的vector的内存释放所以调用一次析构函数释放a,然后函数结束,pointVec(含有a和b)和最初的a,b两个对象被释放,所以调用四次析构函数


拷贝构造函数的调用时机

  • 当函数的参数为类的对象时(这是引用 :Book &book 这是对象:Book book
  • 函数的返回值是类的对象(不是引用)
  • 对象需要通过另外一个对象进行初始化

vector是怎么扩大容量的

现在改一下上面的main函数

int main()
{std::vector<Point> pointVec;Point a;Point b;pointVec.push_back(a);std::cout<<"size="<<pointVec.size()<<std::endl;std::cout<<"capacity="<<pointVec.capacity()<<std::endl;pointVec.push_back(b);std::cout<<"size="<<pointVec.size()<<std::endl;std::cout<<"capacity="<<pointVec.capacity()<<std::endl;pointVec.push_back(b);pointVec.push_back(a);std::cout<<"size="<<pointVec.size()<<std::endl;std::cout<<"capacity="<<pointVec.capacity()<<std::endl;pointVec.push_back(b);   std::cout<<"size="<<pointVec.size()<<std::endl;std::cout<<"capacity="<<pointVec.capacity()<<std::endl;return 0;
}

下面使是输出内容 目的是为了看一下vector怎么分配内存的(注意capacity()容器的容量不等于size())

每次超出 vector都会扩大一倍容量

$ ./test
construction is called
construction is called
copy construction is called
size=1
capacity=1
copy construction is called
copy construction is called
destruction is called
size=2
capacity=2
copy construction is called
copy construction is called
copy construction is called
destruction is called
destruction is called
copy construction is called
size=4
capacity=4
copy construction is called
copy construction is called
copy construction is called
copy construction is called
copy construction is called
destruction is called
destruction is called
destruction is called
destruction is called
size=5
capacity=8
destruction is called
destruction is called
destruction is called
destruction is called
destruction is called
destruction is called
destruction is called


在引入右值引用,转移构造函数,转移复制运算符之前,通常使用push_back()向容器中加入一个右值元素(临时对象)的时候,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放入容器中。原来的临时变量释放。这样造成的问题是临时变量申请的资源就浪费。

原文:https://blog.csdn.net/MOU_IT/article/details/88757348


vector emplace_back和push_back()区别

临时对象创建的时候用emplace_back和push_back()有区别,如果是下面这样则没有区别

#include <iostream>
#include <vector>class Point
{public:Point(){std::cout<<"construction is called"<<std::endl;}Point(const Point& p){std::cout<<"copy construction is called"<<std::endl;}Point(Point&& ohter){  std::cout<<"moveConstruction is called"<<std::endl;}
};int main()
{std::vector<Point> pointVec1;std::vector<Point> pointVec2;Point a;Point b;std::cout<<"push_back(a):"<<std::endl;pointVec1.push_back(a);std::cout<<"emplace_back(a):"<<std::endl;pointVec2.emplace_back(a);std::cout<<"return 0;"<<std::endl;return 0;
}

输出 可以看出这么做并没有区别

$ ./test
construction is called
construction is called
push_back(a):
copy construction is called
emplace_back(a):
copy construction is called
return 0;

而这样 则会有区别:

#include <iostream>
#include <vector>
#include <string>class Point
{public:Point(std::string name,int price)  :name_(name), price_(price){std::cout<<"construction is called"<<std::endl;}Point(const Point& other):name_(other.name_), price_(other.price_){std::cout<<"copy construction is called"<<std::endl;}Point(Point&& other):name_(other.name_), price_(other.price_){std::cout<<"moveConstruction is called"<<std::endl;}public:std::string name_;int price_;
};int main()
{std::vector<Point> pointVec1;std::vector<Point> pointVec2;std::cout<<"push_back:"<<std::endl;pointVec1.push_back(Point("banana",13));std::cout<<"emplace_back:"<<std::endl;pointVec2.emplace_back("apple",14);//注意pusk_back不可以这么用std::cout<<"return 0;"<<std::endl;return 0;
}

输出:

$ ./test
push_back:
construction is called
moveConstruction is called
emplace_back:
construction is called
return 0;

总结:

push_back()右值时就会调用构造函数和转移构造函数。进一步优化的空间就是使用emplace_back,在容器尾部添加一个元素,这个元素原地构造不需要触发拷贝构造和转移构造


std::move()

C++ 标准库使用比如vector::push_back 等这类函数时,会对参数的对象进行复制,连数据也会复制.这就会造成对象内存的额外创建, 本来原意是想把参数push_back进去就行了,通过std::move,可以避免不必要的拷贝操作。

在C++11中,标准库在<utility>中提供了一个有用的函数std::move,std::move并不能移动任何东西,它唯一的功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义。从实现上讲,std::move基本等同于一个类型转换:static_cast<T&&>(lvalue);

std::move函数可以以非常简单的方式将左值引用转换为右值引用。

std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝所以可以提高利用效率,改善性能.。

//摘自https://zh.cppreference.com/w/cpp/utility/move
#include <iostream>
#include <utility>
#include <vector>
#include <string>
int main()
{std::string str = "Hello";std::vector<std::string> v;//调用常规的拷贝构造函数,新建字符数组,拷贝数据v.push_back(str);std::cout << "After copy, str is "" << str << ""n";//调用移动构造函数,掏空str,掏空后,最好不要使用strv.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"


std::move 的函数原型定义

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{return static_cast<typename remove_reference<T>::type&&>(t);
}

原文链接:c++ 之 std::move 原理实现与用法总结

c++ vector拷贝构造_vector------stl学习笔记一相关推荐

  1. C++ STL学习笔记

    C++ STL学习笔记一 为何要学习STL: 数据结构与算法是编程的核心,STL中包含各种数据结构和优秀的算法,确实值得深入学习,本文中虽然着重使用,但希望有心的朋友能多看看相关数据结构的实现,对于C ...

  2. C++STL学习笔记(4) 分配器(Allocator)

    在前面的博客<C++ STL学习笔记(3) 分配器Allocator,OOP, GP简单介绍>中,简单的介绍了分配器再STL的容器中所担当的角色,这一节对STL六大部件之一的分配器进行详细 ...

  3. C++ STL学习笔记(3) 分配器Allocator,OOP, GP简单介绍

    继续学习侯捷老师的课程! 在前面的博客<C++ STL学习笔记(2) 容器结构与分类>中介绍了STL中常用到的容器以及他们的使用方法,在我们使用容器的时候,背后需要一个东西支持对内存的使用 ...

  4. 【C++ STL学习笔记】C++ STL序列式容器(array,vector,deque,list)

    文章目录 C++ STL容器是什么? 迭代器是什么,C++ STL迭代器(iterator)用法详解 迭代器类别 迭代器的定义方式 C++序列式容器(STL序列式容器)是什么 容器中常见的函数成员 C ...

  5. SGI STL 学习笔记二 vector

    sequence containers Array Vector Heap Priority_queue List sList(not in standard) Deque Stack Queue S ...

  6. C++ STL学习笔记(5) Vector容器, array容器,deque容器

    动态增长的数组vector,当它放入的元素满了的时候,会自动的扩充内存,但是,在计算机中内存不能够实现原地扩充,因为在申请了一块固定大小的内存之后,这块内存不管有没有用完,他后面的内存都有可能别的内容 ...

  7. C++ STL学习笔记 : 1. template 模板函数

    本篇文章是学习C++ STL库的第一篇笔记,主要记录了使用template关键字创建模板函数的方法. 下面用一个非常简单的例子解释模板函数的用法 : #include <iostream> ...

  8. C++ STL学习笔记(2) 容器结构与分类

    接着学习侯捷老师的C++ STL! 在使用容器的时候,需要明白容器中元素之间在内存里的关系是什么样的,是连续的,还是非连续的. 容器可以分为两类: 1. sequence container , 即序 ...

  9. C++ STL 学习笔记__(6)优先级队列priority_queue基本操作

    10.2.7优先级队列priority_queue v  最大值优先级队列.最小值优先级队列 v  优先级队列适配器 STL priority_queue v  用来开发一些特殊的应用,请对stl的类 ...

最新文章

  1. 微服务常见安全认证方案Session token cookie跨域
  2. php写poc,xray写POC踩坑
  3. python里clear和copy_python之字典
  4. 《DeepLearning.ai 深度学习笔记》发布,黄海广博士整理
  5. linux升级openssh8.2,openssh7更换升级位8.2版本过程
  6. hosts多个ip对应一个主机名_Ubuntu16.04修改主机名和查看主机名的方法
  7. 基于酷Q的工作秘书机器人
  8. WPF DataGrid 和LINQ to SQL示例程序之一 (提供源代码下载)
  9. 连接手表_荣耀手表2 一键连接你的手上智慧新生活
  10. mysql安装无法创建mysqld_MySQL 5.7安装错误`mysqld:无法创建/写入文件’/ var / lib / mysql / is_writable’...
  11. ARM Mali-V VPU视频处理单元介绍 V61 V550 V500
  12. 2019年‘泰迪杯’数据分析职业技能大赛A题——个人代码分享
  13. c语言中girth的作用,C++初级问题,急救中!!!
  14. JVM---数据存储和访问(类文件结构)
  15. python能解决什么数据问题_浅谈Python数据分析
  16. Day53 Linux setitimer函数 信号集操作函数 信号捕捉 SIGCHLD信号
  17. 四十九、HBase介绍
  18. EasyExcel大批量数据导出OOM,个人案例和解决办法
  19. C# Socket实现两台电脑通信(二)
  20. 本原多项式 M序列和AES不可约多项式

热门文章

  1. 阿里《Java开发手册》中的 1 个bug!
  2. RabbitMQ的元数据重建
  3. 力扣- -231. 2的幂
  4. 区分多种类型的输入输出
  5. 【教育与多媒体技术】
  6. 你今天怎么这么好看——基于深度学习的大型现场实时美颜
  7. LiveVideoStackCon 2020上海 6月见
  8. AI繁荣下的隐忧——Google Tensorflow安全风险剖析
  9. 验证OpenStack安装
  10. Squash my last X commits together using Git | Git 如何合并历史提交记录?