c ++ std :: vector是如何工作的?

时间:2010-07-02 15:58:06

标签: c++ vector

添加和删除元素如何“重新缩放”数据?如何计算矢量的大小(我相信它是跟踪的)?任何其他额外的资源来了解矢量将不胜感激。

5 个答案:

答案 0 :(得分:31)

在调整大小方面,std::vector有两个值:sizecapacity(通过.size().capacity()访问)。

.size()是向量中包含的元素数,而.capacity()是在重新分配内存之前可以添加到向量的元素数。

如果你.push_back()一个元素,大小将增加1,直到你达到容量。一旦达到容量,大多数(全部?)实现,重新分配内存,使容量加倍。

您可以使用.reserve预留容量。例如:

std::vector<int> A;
A.reserve(1);        // A: size:0, capacity:1  {[],x}
A.push_back(0);      // A: size:1, capacity:1  {[0]}
A.push_back(1);      // A: size:2, capacity:2  {[0,1]}
A.push_back(2);      // A: size:3, capacity:4  {[0,1,2],x}
A.push_back(3);      // A: size:4, capacity:4  {[0,1,2,3]}
A.push_back(4);      // A: size:5, capacity:8  {[0,1,2,3,4],x,x,x}

内存的重新分配将在第4,5和7行发生。

答案 1 :(得分:9)

向量通常有三个指针。如果从未使用过向量,则它们都是0或NULL。

  • 一个到矢量的第一个元素。 (这是begin()迭代器)
  • 向量的最后一个元素+ 1.(这是end()迭代器)
  • 还有一个分配但未使用的元素+ 1.(此减去begin()是容量)

插入元素时,向量会分配一些存储并设置其指针。它可能分配1个元素,或者它可能分配4个元素。或者50.

然后它插入元素并递增最后一个元素指针。

当您插入的元素多于分配的元素时,向量必须获得更多内存。它出去了,得到了一些。如果内存位置发生变化,则必须将所有元素复制到新空间并释放旧空间。

调整大小的一个常见选择是每次需要更多内存时将分配加倍。

答案 2 :(得分:3)

std::vector的实现稍微改变了C ++ 0x,随后引入了移动语义(参见What are move semantics?的介绍)。

向已填满的std::vector添加元素时,会调整vector的大小,这涉及分配新的更大内存区域的过程,将现有数据移动到新{{1}删除旧的vector空格,然后添加新元素。

vector是标准模板库中的集合类。将对象放入std::vector,取出它们,或者vector将项目添加到完整vector时执行调整大小都要求对象的类支持赋值运算符,复制构造函数,并移动语义。 (有关详细信息,请参阅type requirements for std::vectorstd::vector works with classes that are not default constructible?。)

考虑vector的一种方法是定义std::vector时指定类型的连续元素的C样式array,它具有一些附加功能以将其集成到标准中模板库产品。 vector与标准vector的区别在于array会随着项目的添加而动态增长。 (有关差异的一些讨论,请参阅std::vector and c-style arrays以及When would you use an array rather than a vector/string?。)

使用vector允许使用其他标准模板库组件(例如算法),因此使用std::vector与C样式std::vector相比具有相当多的优势,因为您可以使用已经存在。

如果提前知道最大值,则可以指定初始大小。 (请参阅Set both elements and initial capacity of std::vector以及Choice between vector::resize() and vector::reserve()

array物理表示的基础是使用从堆分配的内存的一组指针。这些指针允许实际操作访问std::vector中存储的元素,删除vector中的元素,迭代vector,确定元素数量,确定元素大小等

由于物理表示是连续的内存,因此删除项目可能会导致移动剩余项目以关闭删除操作创建的任何漏洞。

使用现代C ++移动语义,vector的开销已经减少,因此它通常是Bjarne Stroustrup在其着作“C ++编程语言第4版”中推荐的大多数应用程序的默认容器。讨论C ++ 11。

答案 3 :(得分:0)

我在大约一年前用C ++编写了一个向量。它是一个具有设定大小(例如16个字符)的数组,在需要时会扩展该数量。也就是说,如果默认大小为16个字符并且您需要存储Hi my name is Bobby,那么它会将数组的大小加倍为32个字符,然后将char数组存储在那里。

答案 4 :(得分:0)

我认为std::vector的基本思想可以通过一个示例来理解:

template<typename T>
class vector {

    T *storage;
    unsigned int length, cap;

    void resizeStorage() {
        int *copy = new T[cap];
        for (unsigned int i = 0 ; i < length, ++i) {
            copy[i] = storage[i]; 
        }
        delete [] storage;
        storage = copy
    }

    public:
    vector(unsigned int cap = 1): length(0), cap(cap), storage(new T[cap]) { 
        if (!cap)
            cap = 1;
    }

    int size() {
        return this.length;
    }

    int capacity() {
        return this.cap;
    }

    T& operator[](int index) {
        return storage[index];
    }

    const T& operator[](int index) const { 
        return storage[index];
    }

    void push_back(T element) {
        reserve(++length);
        storage[length] = element;
    }

    void reserve(int capacity) { 
        if(cap >= capacity) {
            return;
        }
        while(cap < capacity) { 
            cap *= 2;
        }
        resizeStorage();
    }

    virtual ~vector() { 
        delete[] storage;
    }

}

如果存储空间太小,我们需要为每个push_back保留足够的容量。我们也可以手动保留,并在完成后清理内存。

可以使用数组std::copy完成数组复制逻辑,我们也可以使用智能指针进行隐式清理。我选择使用低级API来代替,以演示逻辑步骤。