1. insert插入和emplace的区别
emplace操作是C++11新特性,新引入的的三个成员emlace_front、empace 和 emplace_back,这些操作构造而不是拷贝元素到容器中,这些操作分别对应push_front、insert 和push_back,允许我们将元素放在容器头部、一个指定的位置和容器尾部。
两者的区别
当调用insert时,我们将元素类型的对象传递给insert,元素的对象被拷贝到容器中,而当我们使用emplace时,我们将参数传递元素类型的构造函数,emplace使用这些参数在容器管理的内存空间中直接构造元素。
insert插入的数据需要经过 栈->寄存器->内存 ,而emplace只需要 寄存器->内存。
例子
假定d是一个Date类型的容器。
//使用三个参数的Date构造函数,在容器管理的内存空间中构造新元素。
d.emplace_back(“2016”,”05”,”26”);
//错误,push_back没有这种用法
d.push_back(“2016”,”05”,”26”);
//push_back()创建一个临时对象,然后将临时对象拷贝到容器中
d.push_back(Date(“2016”,”05”,”26”));
通过例子发现,使用c++11新特性emplace向容器中添加新元素,在容器管理的内存空间中构造新元素,与insert相比,省去了构造临时对象,减少了内存开销。
2.iterator迭代器失效问题
2.1 插入失效
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> v;
v.push_back(1);
std::vector<int>::iterator iter1 = v.begin();
v.push_back(1);
int n = *iter1;
cout << n << endl;
return 0;
}
上面的代码运行时崩溃,就是因为迭代器iter1在vector push_back新值后失效了。切记!
但是为什么会失效?
如果我先预先resize足够大,那么会如何呢?
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> v;
v.resize(4);
v.push_back(1);
std::vector<int>::iterator iter1 = v.begin();
v.push_back(1);
int n = *iter1;
cout << n << endl;
return 0;
}
上面的代码正常运行,因为之前resize了足够的空间,即迭代器不会失效。
我们就已给vector push_back两个元素为例。resize(1), resize(2), resize(3)程序还是会崩溃。还是容量不足,导致vector重新分配内存,而导致迭代器失效。
参考博客:http://blog.youkuaiyun.com/wangshubo1989/article/details/50334297
2.2删除失效
#include <iostream>
#include <vector>
using namespace std;
typedef vector<int> Vec;
typedef vector<int>::iterator VecIt;
void print(Vec &v)
{
VecIt it;
for(it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void deleteValueFromVector(Vec &v, int n = 5)
{
VecIt it;
for(it = v.begin(); it != v.end(); it++)
{
if(0 == *it % n)
{
v.erase(it);
}
}
}
int main()
{
Vec v;
int i = 0;
for(i = 0; i < 21; i++)
{
v.push_back(i); // 不能再傻傻地用下标了
}
print(v);
deleteValueFromVector(v); // 程序崩溃
print(v);
return 0;
}
运行程序, 结果程序也崩溃, 可见, vector删除也存在迭代器失效的问题, 怎么改呢? 如下:
#include <iostream>
#include <vector>
using namespace std;
typedef vector<int> Vec;
typedef vector<int>::iterator VecIt;
void print(Vec &v)
{
VecIt it;
for(it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void deleteValueFromVector(Vec &v, int n = 5)
{
VecIt it;
for(it = v.begin(); it != v.end(); /*不能再自增了*/)
{
if(0 == *it % n)
{
v.erase(it); // vector元素自动向前挪动了(关联的map容器元素不会自动挪动), 所以不能再把it进行++了
}
else
{
it++;
}
}
}
int main()
{
Vec v;
int i = 0;
for(i = 0; i < 21; i++)
{
v.push_back(i); // 不能再傻傻地用下标了
}
print(v);
deleteValueFromVector(v); // 程序ok
print(v);
return 0;
}
结果为:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
1 2 3 4 6 7 8 9 11 12 13 14 16 17 18 19
可见, vector迭代器失效的问题也不容忽视。
3.Capacity容量的增长方式
下面我们截取了VS2017编译器中的关于容量增长的源码:
size_type _Calculate_growth(const size_type _ Newsize) const
{ // given _Oldcapacity and _Newsize, calculate geometric growth
const size_type _Oldcapacity = capacity();
if (_Oldcapacity > max_size() - _Oldcapacity / 2)
{
return (_Newsize); // geometric growth would overflow
}
const size_type _Geometric = _Oldcapacity + _Oldcapacity / 2;
if (_Geometric < _Newsize)
{
return (_Newsize); // geometric growth would be insufficient
}
return (_Geometric); // geometric growth is sufficient
}
第一种情况:_Oldcapacity > max_size() - _Oldcapacity / 2 : 当旧容量大于(最大的可能的元素个数 - 旧容量/2) ,直接返回Newsize大小的容量
第二种情况:_Geometric = _Oldcapacity + _Oldcapacity / 2; 即1.5倍旧容量小于Newsize,返回Newsize大小的容量。
第三种情况: 排除以上2种情况,返回1.5倍旧容量
参考博客:
map和vector的迭代器失效问题:
http://www.cnblogs.com/yuanshuang/p/5777905.html
实战c++中的vector系列–可怕的迭代器失效(vector重新申请内存):
http://blog.youkuaiyun.com/wangshubo1989/article/details/50334297
C++11新特性emplace操作:
http://blog.youkuaiyun.com/penghuicheng/article/details/51505625