【问题标题】:How is dynamic memory managed in std::vector?std::vector 中如何管理动态内存?
【发布时间】:2010-10-14 21:56:43
【问题描述】:

std::vector如何实现对元素数量变化的管理:是使用realloc()函数,还是使用链表?

谢谢。

【问题讨论】:

    标签: c++ memory-management stl vector


    【解决方案1】:

    它使用分配给它的分配器作为第二个模板参数。那就这样吧。假设在push_back中,设t为要推送的对象:

    ...
    if(_size == _capacity) { // size is never greater than capacity
        // reallocate
        T * _begin1 = alloc.allocate(_capacity * 2, 0);
        size_type _capacity1 = _capacity * 2;
    
        // copy construct items (copy over from old location).
        for(size_type i=0; i<_size; i++)
            alloc.construct(_begin1 + i, *(_begin + i));
        alloc.construct(_begin1 + _size, t);
    
        // destruct old ones. dtors are not allowed to throw here. 
        // if they do, behavior is undefined (17.4.3.6/2)
        for(size_type i=0;i<_size; i++)
            alloc.destroy(_begin + i);
        alloc.deallocate(_begin, _capacity);
    
        // set new stuff, after everything worked out nicely
        _begin = _begin1;
        _capacity = _capacity1;
    } else { // size less than capacity
        // tell the allocator to allocate an object at the right
        // memory place previously allocated
        alloc.construct(_begin + _size, t);
    }
    _size++; // now, we have one more item in us
    ...
    

    类似的东西。分配器将关心分配内存。它将分配内存和将对象构造到该内存中的步骤分开,因此它可以预分配内存,但还不能调用构造函数。在重新分配期间,向量必须注意复制构造函数抛出的异常,这使事情变得有些复杂。以上只是一些伪代码 sn-p - 不是真正的代码,可能包含许多错误。如果大小超过容量,它会要求分配器分配一个新的更大的内存块,如果没有,那么它只是在先前分配的空间上构造。

    它的确切语义取决于分配器。如果是标准分配器,构造会做

    new ((void*)(_start + n)) T(t); // known as "placement new"
    

    而分配allocate 只会从::operator new 获得内存。 destroy 会调用析构函数

    (_start + n)->~T();
    

    所有在分配器后面抽象出来的东西,向量只使用它。堆栈或池分配器的工作方式可能完全不同。关于vector的一些关键点很重要

    • 调用reserve(N) 后,您最多可以将N 个项目插入到您的向量中,而不会冒重新分配的风险。在那之前,只要size() &lt;= capacity(),对其元素的引用和迭代器仍然有效。
    • Vector 的存储是连续的。您可以将 &v[0] 视为一个缓冲区,其中包含您当前在向量中拥有的尽可能多的元素。

    【讨论】:

      【解决方案2】:

      std::vector 将数据存储在连续的内存块中。

      假设我们将一个向量声明为

      std::vector intvect;

      所以最初将创建 x 个元素的内存。这里 x 取决于实现。

      如果用户插入的元素超过 x 个,则将创建一个包含 2x(两倍大小)元素的新内存块,并将初始向量复制到该内存块中。

      这就是为什么总是建议通过调用reserve来为vector预留内存 功能。

      intvect.reserve(100);

      以免删除和复制矢量数据。

      【讨论】:

        【解决方案3】:

        向量的硬性规则之一是数据将存储在一个连续的内存块中。

        这样你就知道理论上你可以做到这一点:

        const Widget* pWidgetArrayBegin = &(vecWidget[0]);
        

        然后您可以将 pWidgetArrayBegin 传递给需要数组作为参数的函数。

        唯一的例外是 std::vector 特化。它实际上根本不是布尔值,但那是另一回事了。

        所以 std::vector 会重新分配内存,不会使用链表。

        这意味着您可以通过以下方式射中自己的脚:

        Widget* pInteresting = &(vecWidget.back());
        vecWidget.push_back(anotherWidget);
        

        众所周知,push_back 调用可能导致向量将其内容转移到一个全新的内存块,从而使 pInteresting 无效。

        【讨论】:

          【解决方案4】:

          std::vector 管理的内存保证是连续的,因此您可以将&amp;vec[0] 视为指向动态数组开头的指针。

          鉴于此,它实际管理其重新分配的方式是特定于实现的。

          【讨论】:

            猜你喜欢
            • 2010-10-14
            • 1970-01-01
            • 2015-01-25
            • 2018-05-26
            • 1970-01-01
            • 1970-01-01
            • 2018-12-13
            • 2014-03-26
            • 1970-01-01
            相关资源
            最近更新 更多