首先,我们先了解一下vector的工作过程:你创建了一个vector,然后你开始push_back元素,也就是向数组中添加元素;如果vector的容量不够大,不能容纳你想要的新元素,vector需要分配新的内存,至少足够容纳这些想要加入的新元素。当前的vector的内容,从内存中的旧位置复制到内存中的新位置,然后删除掉旧位置的内存。这也就是将代码拖慢的原因之一,也就是我们需要不断地重新分配,这是一个缓慢的操作,我们需要复制所有现有元素,然后重新分配。
这也就是我们目前优化的方向——如何避免复制对象。
#include <iostream>
#include <string>
#include <vector>
struct Vertex
{
float m_x, m_y, m_z;
Vertex(float x, float y, float z)
: m_x(x), m_y(y), m_z(z)
{
}
Vertex(const Vertex& vertex)
:m_x(vertex.m_x), m_y(vertex.m_y), m_z(vertex.m_z)
{
std::cout << "Copyed!" << std::endl;
}
};
std::ostream& operator<<(std::ostream& stream, const Vertex& vertex)
{
stream << vertex.m_x << "," << vertex.m_y << "," << vertex.m_z;
return stream;
}
int main()
{
std::vector<Vertex> vertices(3);
vertices.push_back({1, 2, 3});
vertices.push_back({4, 5, 6});![请添加图片描述](https://img-blog.csdnimg.cn/b8092d9295a24234bd398a1acf323988.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQmFsYWFhbQ==,size_20,color_FFFFFF,t_70,g_se,x_16)
vertices.push_back({7, 8, 9});
return 0;
}
上面代码,最后的运行结果如下:
我们可以看到拷贝复制执行了四次。
第29行vertices.push_back({1, 2, 3});
可等为vertices.push_back({Vertex(1,2,3)})
,我们在栈空间构造了一个vertex对象,然后将其复制到实际的vector创建内存空间中,调用了第一次拷贝构造函数;之后的第30行,因为vector的容量不够,所以vector进行了扩容,将第一次的对象进行了一次拷贝复制,这是第二次调用拷贝构造函数,然后又将第二次创建的对象复制到了实际vector创建的内存空间中,这就是第三次;最后到第31行,将第一个、第二个对象分别进行了一次拷贝,并将第三个对象进行了拷贝至内存空间中。
另一种解释:
vector扩容因子为1.5,初始的capacity为0。第一次push_back,capacity扩容到1,临时对象拷贝到真正的vertices所占内存中,第一次Copied;第二次push_back,发生扩容,capacity扩容到2,vertices发生内存搬移发生的拷贝为第二次Copied,然后再是临时对象的搬移,为第三次Copied;接着第三次push_back,capacity扩容到3(2*1.5 = 3,3之后是4,4之后是6…),vertices发生内存搬移发生的拷贝为第四和第五个Copied,然后再是临时对象的搬移为第六个Copied;
优化步骤一:让编译器知道我们要存储三个对象,即拥有足够的存储容量。
优化步骤二:直接在容器尾部构造元素,省略拷贝过程。
#include <iostream>
#include <string>
#include <vector>
struct Vertex
{
float m_x, m_y, m_z;
Vertex(float x, float y, float z)
: m_x(x), m_y(y), m_z(z)
{
}
Vertex(const Vertex& vertex)
:m_x(vertex.m_x), m_y(vertex.m_y), m_z(vertex.m_z)
{
std::cout << "Copyed!" << std::endl;
}
};
std::ostream& operator<<(std::ostream& stream, const Vertex& vertex)
{
stream << vertex.m_x << "," << vertex.m_y << "," << vertex.m_z;
return stream;
}
int main()
{
std::vector<Vertex> vertices;
vertices.reserve(3); //分配足够的内存来存储三个vertex对象
vertices.emplace_back({1, 2, 3}); //传递的不是我们已经构造好了的vertex对象,而是传递了构造函数的参数列表(实际上就是告诉vector,在实际的vector内存中,使用以下参数,构造一个vertex对象。)
vertices.emplace_back({4, 5, 6});
vertices.emplace_back({7, 8, 9});
return 0;
}
reserve
提前申请内存,避免动态申请开销;
emplace_back
直接在容器尾部创建元素,省略拷贝或移动过程。
最后程序运行结果:没有调用拷贝构造函数。