动态数组的原理
当元素个数比本身的容器容量多时,会自动创建一个更大的容器,并将原容器中的元素全都拷贝进去,然后删掉原容器。
以下面结构体为例
struct Vertex
{
float x, y, z;
};
ostream& operator << (ostream& stream, const Vertex& vertex)
{
stream << vertex.x << ", " << vertex.y << ", " << vertex.z;
return stream;
}
vector基本操作
构造这个结构体的动态数组:(<>用来指定类型,可以是原始数据类型)
vector<Vertex> vertices;
vector<Vertex*> vertices;
一般情况下推荐直接存对象本身,迫不得已存指针类型。vector是连续空间的线性存储,存对象时,对其中元素的遍历或修改等操作会更快。
遍历方式:
两种都可,但注意第二种要引用,否则每次都会创建一个v并拷贝对应元素。
for (int i = 0; i < vertices.size(); i++)
cout << vertices[i] << endl;
for (Vertex& v: vertices)
cout << v << endl;
其他操作:
# 添加元素,{1,2,3}调用了默认的构造函数
vertices.push_back({1, 2, 3});
# 删除索引是1的元素
vertices.erase(vertices.begin()+1);
# 清空数组
vertices.clear();
对于使用该数组的函数:参数一定要使用引用,否则将拷贝整个数组
void Func(const vector<Vertex>& vertices)
{
}
vector优化
优化的目的就是减少拷贝。
将上述Vertex类添加拷贝构造函数的输出,通过单步调试可以查看拷贝情况:
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)
{
cout<< "copied" << endl;
}
};
int main()
{
vector<Vertex> vertices;
vertices.push_back(Vertex(1, 2, 3));
vertices.push_back(Vertex(4, 5, 6));
vertices.push_back(Vertex(7, 8, 9));
// 一共输出了6次copied
}
为什么会输出6次copied?
- 上述添加元素使用的是push_back,也就是将创建好的对象放入数组,这意味着首先要在main中创建了一个对象,再将他拷贝到数组中。
- 调试过程中可以发现数组的大小一开始是0,然后是1,2,3,也就是数组的大小是动态调整的,这需要将原来数组的元素拷贝到新数组,然后删除原数组。
因此整个过程为:
- 首先创建了一个size为0的vector。
- 创建size为1的vector,删除size为0的vector。创建一个Vertex对象(1,2,3),将他拷贝到vector中。总计1次拷贝
- 创建size为2的vector,将size为1的vector中的Vertex(1,2,3)复制到size为2的vector中,删除size为1的vector。创建Vertex对象(4,5,6),将其拷贝到size为2的vector中。总计2次拷贝。
- 同理,总计3次拷贝。
解决办法
- 使用emplace_back()在vector内直接创建对象,参数为构造函数所需的参数列表
- 使用reserve()为vector预先指定一个大小
vector<Vertex> vertices;
vertices.reserve(3);
vertices.emplace_back(1, 2, 3);
vertices.emplace_back(4, 5, 6);
vertices.emplace_back(7, 8, 9);