连续存储结构,每个元素在内存上是连续的,每个元素有固定的位置,取决于插入的顺序和时机;支持 高效的随机访问和在尾端插入/删除操作,但其他位置的插入/删除操作效率低下; 相当于一个数组(本质上是一个动态数组),但是与数组的区别为:内存空间的扩展。vector支持不指定vector大小的存储,但是数组的扩展需要程序员自己写。
#include
#include
struct Vertex
{
float x, y, z;
};
//<<操作符重载
std::ostream& operator<<(std::ostream& stream, const Vertex& vertex)
{
stream << vertex.x << "," << vertex.y << "," << vertex.z;
return stream;
}
int main()
{
std::vector<Vertex> vertices;
vertices.push_back({ 1, 2, 3 });
vertices.push_back({ 4, 5, 6 });
vertices.push_back({ 7, 8, 9 });
for (int i = 0; i < vertices.size(); ++i)
{
std::cout << vertices[i] << std::endl;
}
vertices.erase(vertices.begin() + 1);
vertices.clear();
std::cin.get();
}
vector的使用非常简单,但是实际操作时,这样的push_back是会一直重新分配,数据量特别大的时候,就会很浪费时间,所以实际使用时为了获得最好的性能表现,我们可以对vector的使用进行优化。在了解这些之前,我们先来了解一下vector的扩容机制。
1、新增元素:Vector通过一个连续的数组存放元素,如果集合已满,在新增数据的时候,就要分配一块更大的内存,将原来的数据复制过来,释放之前的内存,在插入新增的元素;
2、对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了 ;
3、初始时刻vector的capacity为0,塞入第一个元素后capacity增加为1;
4、不同的编译器实现的扩容方式不一样,VS2015中以1.5倍扩容,GCC以2倍扩容。
这里的重点就是第1条,我们在动态的增加vector的大小时,实际上会进行频繁的重新分配和拷贝,数据量特别大时会拖慢程序的运行速度。看一下下面的一个例子:
#include
#include
struct Vertex
{
float x, y, z;
Vertex(float x, float y, float z)
: x(x), y(y), z(z) {}
//拷贝构造函数
Vertex(const Vertex& vertex)
: x(vertex.x), y(vertex.y), z(vertex.z)
{
std::cout << "Copied!" << std::endl;
}
};
//<<操作符重载
std::ostream& operator<<(std::ostream& stream, const Vertex& vertex)
{
stream << vertex.x << "," << vertex.y << "," << vertex.z;
return stream;
}
int main()
{
std::vector<Vertex> vertices;
vertices.push_back({ 1, 2, 3 });
vertices.push_back({ 4, 5, 6 });
vertices.push_back({ 7, 8, 9 });
std::cin.get();
}
输出:
如上图所示,我们仅仅push_back了三次,就拷贝了6次,那如果一直push_back下去10000次或者1000000次呢,那么拷贝的次数会以指数增长,这是拖慢程序运行的原因之一!
我们需要明白实际的拷贝发生在哪里,首先,在我们push_back元素至vector分配的内存中这发生了一次拷贝,然后,在不断的push_back中,当vector容量不够时会进行扩容,根据扩容机制1就要分配一块更大的内存,将原来的数据复制过来,释放之前的内存,这就又发生了一次拷贝。
****优化思想:避免不断地拷贝
1、如果我们构造Vertex时就在vector事先分配好的那块内存中;
2、如果我们了解事先vector的容量,为什么不直接申请能够存放3个Vertex的内存,这样就不必扩容两次的,也就减少了拷贝次数。
在此之前我们先要了解vector的reserve和resize的区别!
vector 的reserve增加了vector的capacity,但是它的size没有改变!而resize改变了vector的capacity同时也增加了它的size!
原因如下:
1、reserve是容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新的对象之前,不能引用容器内的元素。加入新的元素时,要调用push_back()/insert()函数。
2、resize是改变容器的大小,且在创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。此时再调用push_back()函数,是加在这个新的空间后面的。
#include
#include
struct Vertex
{
float x, y, z;
Vertex(float x, float y, float z)
: x(x), y(y), z(z) {}
//拷贝构造函数
Vertex(const Vertex& vertex)
: x(vertex.x), y(vertex.y), z(vertex.z)
{
std::cout << "Copied!" << std::endl;
}
};
//<<操作符重载
std::ostream& operator<<(std::ostream& stream, const Vertex& vertex)
{
stream << vertex.x << "," << vertex.y << "," << vertex.z;
return stream;
}
int main()
{
std::vector<Vertex> vertices;
vertices.reserve(3);//事先分配vertices的容量为3
vertices.push_back({ 1, 2, 3 });
vertices.push_back({ 4, 5, 6 });
vertices.push_back({ 7, 8, 9 });
std::cin.get();
}
这里仅比上面使用了reserve函数事先分配vertices的容量为3,输出
可以看出,此时就仅有三个拷贝了,这样我们进行优化思想的第二条,接下来看第一条如何进行优化。仅仅需要把push_back改成emplace_back
vertices.emplace_back(1, 2, 3 );
vertices.emplace_back(4, 5, 6 );
vertices.emplace_back(7, 8, 9 );
在这种情况下,不是传递我们已经构建的vertex对象,只是传递了构造函数的参数列表,它告诉vector在实际的vector内存中,使用以下参数构建一个vertex对象。
输出
此时什么也没有输出,也就是没有发生拷贝,可以提高程序的运行速度。
emplace_back() 和 push_back() 的区别,就在于底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。