[英]How does dynamic arrays(std::vector) work in c++?
据我所知,std::vector 连续存储数据,当您尝试向其添加或删除一个元素时,它会通过分配新的 memory 来增长或缩小,并将所有内容从旧的 memory 复制到新的 memory 并进行更改并删除旧的 memory
在下面的代码中,我有一个复制构造函数,它说每次复制 class 时都会复制它,当我有 3 个元素时,它给了我 6 个复制的消息,这很好,但是当我添加另一个元素时,它给了我 7 个消息
不是应该给我10吗? 以及如何优化它?
#include <iostream>
#include <vector>
struct Vertex
{
int x, y, z;
Vertex(int x, int y, int z) : x(x), y(y), z(z)
{
}
Vertex(const Vertex &vetrex) : x(vetrex.x), y(vetrex.y), z(vetrex.z)
{
std::cout << "Copied" << std::endl;
}
};
int main()
{
std::vector<Vertex> vertices;
vertices.push_back({1, 2, 3});
vertices.push_back({4, 5, 6});
vertices.push_back({7, 8, 9});
vertices.push_back({10, 11, 12});
}
据我所知,
std::vector
连续存储数据,当您尝试向其添加或删除一个元素时,它会通过分配新的 memory 来增长或缩小,并将所有内容从旧的 memory 复制到新的 memory 并进行更改并删除旧的 memory
std::vector<T>
实际上并不是每次向其添加元素时都增长。 它有一个叫做容量的概念(参见std::vector<T>::capacity()
)。
每次添加元素时增长或收缩的实际数字是std::vector<T>::size()
,它只是容量的限制边界。 容量实际上告诉你当时分配了多少元素。
本质上,在大多数实现中, std::vector<T>
的容量以 2 的幂增长。(这样做背后有一个原因,这主要与性能有关。)
因此,检查每个push_back()
调用中的容量以查看:
int main()
{
std::vector<Vertex> vertices;
std::cout << "Size: " << vertices.size() << std::endl; // Size: 0
std::cout << "Capacity: " << vertices.capacity() << std::endl; // Capacity: 0
vertices.push_back({1, 2, 3}); // 1 copy
std::cout << "***" << std::endl;
std::cout << "Size: " << vertices.size() << std::endl; // Size: 1
std::cout << "Capacity: " << vertices.capacity() << std::endl; // Capacity: 2^0 = 1
vertices.push_back({4, 5, 6}); // 2 copies (due to reallocation)
std::cout << "***" << std::endl;
std::cout << "Size: " << vertices.size() << std::endl; // Size: 2
std::cout << "Capacity: " << vertices.capacity() << std::endl; // Capacity: 2^1 = 2
vertices.push_back({7, 8, 9}); // 3 copies (due to reallocation)
std::cout << "***" << std::endl;
std::cout << "Size: " << vertices.size() << std::endl; // Size: 3
std::cout << "Capacity: " << vertices.capacity() << std::endl; // Capacity: 2^2 = 4
vertices.push_back({10, 11, 12}); // 1 copy
std::cout << "***" << std::endl;
}
如您所见,在您第四次调用push_back()
时, vertices
的容量为4
是,它的大小为3
,所以它很方便地只做了一个副本,而不必重新分配任何东西,因为已经有足够的空间。
以及如何优化它?
如果您事先已经知道要添加多少元素,则可以在任何push_back()
调用之前调用std::vector<T>::reserve()
:
vertices.reserve(4);
这避免了任何类型的重新分配,只需复制 4 份。
让我们逐步了解它:
std::vector<Vertex> vertices;
此时,capacity为0,size为0。
vertices.push_back({1, 2, 3});
Vector 分配一个大小为 1 的缓冲区来保存元素。 将传递给push_back
的构造参数复制到向量中 [1/7 副本]
vertices.push_back({4, 5, 6});
矢量容量为 1,大小为 1。它已满,因此要添加第二个元素,它会分配一个新的双倍大小的缓冲区(现在为 2)。
push_back
的构造参数复制到向量中 [3/7 副本]然后:
vertices.push_back({7, 8, 9});
向量容量为 2,大小为 2。它再次变满并通过分配大小为 4 的缓冲区将其缓冲区大小加倍。
push_back
的参数复制到向量 [6/7] 份中然后:
vertices.push_back({10, 11, 12});
向量容量为 4,大小为 3。它有空间容纳新元素而无需调整大小。
push_back
的参数复制到向量 [7/7] 份中请注意,矢量“加倍”很常见,但不是标准所要求的。 实现可以调整为其他数量,因此不依赖于预期重新分配大小。
此外,为了减少重新分配的次数,您可以在所有插入之前保留大小。 在那种情况下,它的效率要高得多:
std::vector<Vertex> vertices;
vertices.reserve(4);
如果您在开头添加为 4 个元素保留空间的调用,则只有 4 个副本——每个添加到向量中的元素一个。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.