C ++教科书和线程(如these)表示向量元素在内存中是物理上连续的。
但是当我们执行v.push_back(3.14)
之类的操作时,我会假设STL正在使用new
运算符来获取更多内存来存储刚刚引入向量中的新元素3.14。
现在说大小为4的矢量存储在标有0x7, 0x8, 0x9, 0xA
的计算机存储单元中。如果单元格0xB
包含其他一些不相关的数据,那么3.14
将如何进入此单元格?这是否意味着单元格0xB
将被复制到其他地方,删除以便为3.14
腾出空间?
答案 0 :(得分:10)
简短的回答是,包含矢量数据的整个数组会移动到有空间增长的位置。向量类保留的数组大于技术上保持向量中元素数量所需的数组。例如:
vector< int > vec;
for( int i = 0; i < 100; i++ )
vec.push_back( i );
cout << vec.size(); // prints "100"
cout << vec.capacity(); // prints some value greater than or equal to 100
capacity()
方法返回向量保留的数组的大小,而size()
方法返回数组中实际使用的元素数。 capacity()
将始终返回大于或等于size()
的数字。您可以使用reserve()
方法更改后备阵列的大小:
vec.reserve( 400 );
cout << vec.capacity(); // returns "400"
请注意,size()
,capacity()
,reserve()
和所有相关方法都是指向量所持有的类型的各个实例。例如,如果vec
的类型参数T是一个占用10
字节内存的结构,那么vec.capacity()
返回400
意味着该向量实际上具有4000
保留的内存字节数(400 x 10 = 4000
)。
那么如果将更多元素添加到向量而不是它具有容量,会发生什么呢?在这种情况下,向量会分配一个新的后备数组(通常是旧数组大小的两倍),副本旧数组到新数组,然后释放旧数组。在伪代码中:
if(capacity() < size() + items_added)
{
size_t sz = capacity();
while(sz < size() + items_added)
sz*=2;
T* new_data = new T[sz];
for( int i = 0; i < size(); i++ )
new_data[ i ] = old_data[ i ];
delete[] old_data;
old_data = new_data;
}
因此整个数据存储被移动到内存中的新位置,该位置具有足够的空间来存储当前数据以及许多新元素。如果分配的空间比实际需要的多得多,则某些向量也可能动态地减小其后备阵列的大小。
答案 1 :(得分:8)
std::vector
首先分配一个更大的缓冲区,然后将现有元素从“旧”缓冲区复制到“新”缓冲区,然后删除“旧缓冲区”,最后将新元素添加到“新缓冲区”中“缓冲区。
通常,std::vector
实现通过在每次分配更大缓冲区所需的容量增加时增加其内部缓冲区。
正如Chris所提到的,每次缓冲区增长时,所有现有的迭代器都会失效。
答案 2 :(得分:5)
当std :: vector为值分配内存时,它会分配超出需要的内存;你可以通过致电capacity
了解多少。当该容量用完时,它会分配一个更大的块,同样大于它需要的块,并复制从旧内存到新内存的所有内容;然后它释放旧记忆。
答案 3 :(得分:1)
如果没有足够的空间来添加新元素,将分配更多空间(正如您正确指出的那样),旧数据将被复制到新位置。因此单元格0xB仍然包含旧值(因为它可能在其他地方有指针,在不造成破坏的情况下移动它是不可能的),但是整个向量将被移动到新位置。
答案 4 :(得分:0)
在C ++中,来自C,内存不是按照你描述的方式“管理” - Cell 0x0B
的内容不会被移动。如果你这样做,任何现有的指针将被无效! (唯一可行的方法是,如果语言没有指针,只使用类似功能的引用。)
std::vector
分配一个新的更大的缓冲区,并将3.14
的值存储到缓冲区的“end”。
但是,对于优化的this->push_back()
,通常std::vector
会将内存分配为this->size()
的两倍。这可确保为性能交换合理数量的内存。因此,我们无法保证3.14
会导致this->resize()
,并且仅当this->buffer[this->size()++]
时才会被放入this->size() < this->capacity()
。
答案 5 :(得分:0)
向量是一个内存数组。典型的实现是它占用的内存超过了所需的内存。足迹需要扩展到任何其他内存 - 整个批次都被复制。旧的东西被释放了。向量存储器位于堆栈中 - 应该注意。说最大尺寸是一个好主意。