【问题标题】:basic question on std::vector in C++C++ 中关于 std::vector 的基本问题
【发布时间】:2011-09-08 22:44:56
【问题描述】:

C++ 教科书和线程,如these 说向量元素在内存中物理上是连续的。

但是当我们执行v.push_back(3.14) 之类的操作时,我会假设 STL 正在使用 new 运算符来获取更多内存来存储刚刚引入向量中的新元素 3.14。

现在假设大小为 4 的向量存储在标记为 0x7, 0x8, 0x9, 0xA 的计算机内存单元中。如果单元格0xB 包含一些其他不相关的数据,3.14 将如何进入此单元格?这是否意味着单元格0xB 将被复制到其他地方,被删除以腾出空间给3.14

【问题讨论】:

标签: c++ vector


【解决方案1】:

简短的回答是保存向量数据的整个数组被移动到一个有空间增长的位置。向量类保留了一个比技术上需要的更大的数组来保存向量中的元素数量。例如:

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;
}

因此,整个数据存储被移动到内存中的一个新位置,该位置有足够的空间来存储当前数据和一些新元素。如果分配的空间比实际需要的多得多,某些向量也可能会动态减小其后备数组的大小。

【讨论】:

  • 在伪代码中你的意思是 if( capacity()
  • @Ajeet - 不,因为 while 循环甚至适用于任意数量的 items_add。例如,考虑 capacity() = 10 和 items_added = 12000 的情况。一个简单的 if 会导致数组溢出。
  • @Chris Vig:不应该是这样 size_t sz = capacity(); while(sz
  • Ajeet - 是的,公平点。我发布的内容远非优化算法,我只是想举例说明幕后可能发生的事情。我同意您的实施更好。
【解决方案2】:

std::vector 首先分配一个更大的缓冲区,然后将现有元素从“旧”缓冲区复制到“新”缓冲区,然后删除“旧缓冲区”,最后将新元素添加到“新”缓冲区中.

通常,std::vector 实现会在每次需要分配更大的缓冲区时通过将容量翻倍来增加其内部缓冲区。

正如 Chris 所说,每次缓冲区增长时,所有现有的迭代器都会失效。

【讨论】:

  • 另外,所有现有的迭代器都失效了。 :-)
  • 但是只有在超过容量时才会分配。您可以使用reserve 定义特定容量。如果您从未超过该容量,则不会发生重新分配或迭代器失效。
【解决方案3】:

当 std::vector 为值分配内存时,它分配的比它需要的多;您可以致电capacity 了解具体金额。当该容量用完时,它会分配一个更大的块,再次大于它的需要,并将旧内存中的所有内容复制到新内存;然后它会释放旧内存。

【讨论】:

    【解决方案4】:

    如果没有足够的空间来添加新元素,则会分配更多空间(如您正确指出的那样),并将旧数据复制到新位置。所以单元格 0xB 仍将包含旧值(因为它可能在其他地方有指向它的指针,所以不可能在不造成破坏的情况下移动它),但有问题的整个向量将被移动到新位置。

    【讨论】:

    • 虽然这是真的,但您完全无法理解 OP 的问题。
    • 我的意思是它将被重新分配到其他地方,这些地方对于向量来说是足够的内存。我错过了什么?
    • 您错过了在其他地方重新分配的部分,以及将数据复制到新分配并相应地重新设置指针的部分。即,首先解释为什么“slot 0xB”中的内容无关紧要的部分。即,实际回答问题的部分。
    • 哦,我认为“将分配更多空间”会很清楚 - 否则没有任何意义。也许我应该更详细一点。
    • OP 已经提到了使用“新”的假设,所以如果很清楚,OP 一开始就不会有问题。
    【解决方案5】:

    向量是一个内存数组。典型的实现是它占用了比需要更多的内存。它的足迹需要扩展到任何其他内存 - 整个批次都被复制了。旧的东西被释放了。向量内存在堆栈上 - 应该注意这一点。说需要最大大小也是一个好主意。

    【讨论】:

    • 向量内存是……什么?
    【解决方案6】:

    在来自 C 的 C++ 中,内存不是您所描述的“管理”方式 - 单元格 0x0B 的内容不会被移动。如果你这样做了,任何现有的指针都会失效! (这可能的唯一方法是,如果语言没有指针并且只使用类似功能的引用。)

    std::vector 分配一个新的更大的缓冲区并将3.14 的值存储到缓冲区的“末尾”。

    不过,通常,对于优化的this-&gt;push_back()s,std::vector 分配的内存大约是this-&gt;size() 的两倍。这可确保为性能交换合理数量的内存。因此,不能保证3.14 会导致this-&gt;resize(),并且当且仅当this-&gt;size() &lt; this-&gt;capacity() 时才可以简单地放入this-&gt;buffer[this-&gt;size()++]

    【讨论】:

      猜你喜欢
      • 2022-09-30
      • 1970-01-01
      • 2013-04-20
      • 1970-01-01
      • 1970-01-01
      • 2021-10-30
      • 1970-01-01
      • 1970-01-01
      • 2018-01-30
      相关资源
      最近更新 更多