Advertisement

你知道vector底层是如何实现的吗?

阅读量:
你知道vector底层是如何实现的吗?

vector底层使用动态数组来存储元素对象,同时使用sizecapacity记录当前元素的数量和当前动态数组的容量。如果持续的push_back(emplace_back)元素,当size大于capacity时,需要开辟一块更大的动态数组,并把旧动态数组上的元素搬移到当前动态数组,然后销毁旧的动态数组。

你知道新开辟的动态数组的容量是就数组的多少倍比较合适?

这个值在不同的编译器上不是固定的。MSVC 是1.5,而GCC是2。

有没有什么好的办法提升vector连续插入效率?

如果知道数据的大概量,我们可以使用reserve方法直接为vector扩容这个量级。这样在后续的数据插入时就不会因为频繁的capacity被用尽而导致的多次的数据搬移,从而提升vector插入效率。

push_backemplace_back有什么区别?

两者都可以在容器尾部插入元素。在GCC中,如果插入的元素是右值,两者都会move元素到容器。如果是左值,两者都会copy元素到容器。唯一不同的一点是,当C++版本高于C++17时,emplace_back返回当前插入的值的引用,而push_back返回void

eraseremove有什么区别?

erase属于成员函数,erase删除了元素,remove属于算法库函数,而remove只会把元素移动到尾部。

哪些情况下迭代器会失效?

迭代器失效主要有两种情况引起:

1.插入数据。由于插入数据可能导致数据搬移(size > capacity),所以迭代器失效。

2.删除数据。当使用erase删除数据时,被删除数据后面的数据依次向前移一位。这会导致被删除数据之后的迭代器失效。

如何快速的清空vector容器并释放vector容器所占用的内存?

有两种方法:

  • 第一种,使用clear方法清空所有元素。然后使用shrink_to_fit方法把capacitysize(0)对齐,达到释放内存的作用;
  • 第二种,使用swap方法;
你知道vector<bool>是如何实现的吗?

vector<bool>的实现和其他实现容器的实现不一致。**每个元素被当作一个位而不是一个字节存储。**这导致我们不能直接访问该元素,也无法对每个元素取地址(8个元素可能在同一个字节中存储)。所以不建议使用vector<bool>,必要时可以使用std::bitset替代。

介绍三个和大小、容量与内存相关的函数:
  • 一个是_M_shrink_to_fit函数,可以使用这个函数将_M_finish和_M_end_of_storage之间的内存释放掉,正好使分配的内存大小满足vector中元素量。
  • 一个是reserve函数,可以使用这个函数控制整个vector容量的作用,避免重新分配内存,例如我们如果知道元素最多有80个,我们就可以使用v.reserve(80)来一次分配80个内存。但是值得注意的是这个函数不能用来缩减容量
  • 一个是resize函数,这个函数实际就是改变_M_start和_M_finish之间的相对位置,改变的是元素的个数,如果resize(n)的n小于当前个数就删除元素,如果大于就追加默认函数生成的对象。
vector是怎么变长的
复制代码
    typename vector<_Tp, _Alloc>::iterator
    vector<_Tp, _Alloc>::insert(iterator __position, const value_type& __x)
    {
    const size_type __n = __position – begin();
    //如果还有空位,插入到最后一个位置,相当于push_back
    if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage
        && __position == end())
    {
        _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x);
        ++this->_M_impl._M_finish;
    }
    else
    {
        _M_insert_aux(__position, __x);
    }
    return iterator(this->_M_impl._M_start + __n);
    }
    
    
    c++
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/A5NcDtIxvUjP0omuXYe8SwdiZbQp.png)

当容量不足的时候,就是_M_insert_aux函数发挥的时候,进行容量的扩展,实现如下:

复制代码
    template<typename _Tp, typename _Alloc>
    void vector<_Tp, _Alloc>::_M_insert_aux(iterator __position, const _Tp& __x)
    {
    //内存空间足够
    if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
    {
        _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
                                _GLIBCXX_MOVE(*(this->_M_impl._M_finish
                                                - 1)));
        ++this->_M_impl._M_finish;
        //__position后的元素依次向后移动一个位置
        _GLIBCXX_MOVE_BACKWARD3(__position.base(),
                                this->_M_impl._M_finish - 2,
                                this->_M_impl._M_finish – 1);
        //目标地址赋值
        *__position = _Tp(std::forward<_Args>(__args)...);
    }
    else
    {
        //内存加倍
        const size_type __len =
        _M_check_len(size_type(1), "vector::_M_insert_aux");
        const size_type __elems_before = __position - begin();
        pointer __new_start(this->_M_allocate(__len));
        pointer __new_finish(__new_start);
        __try
        {
            //给position位置赋值
            _Alloc_traits::construct(this->_M_impl,
                                    __new_start + __elems_before,
                                    std::forward<_Args>(__args)...);
                                    __x);
            __new_finish = 0;
            //拷贝position位置前元素
            __new_finish = std::__uninitialized_move_if_noexcept_a
            (this->_M_impl._M_start, __position.base(),
                __new_start, _M_get_Tp_allocator());
    
            ++__new_finish;
            //拷贝position位置后元素
            __new_finish
            = std::__uninitialized_move_if_noexcept_a
            (__position.base(), this->_M_impl._M_finish,
                __new_finish, _M_get_Tp_allocator());
            }
        __catch(...)
        {
            if (!__new_finish)
            _Alloc_traits::destroy(this->_M_impl,
                                    __new_start + __elems_before);
            else
            std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator());
            _M_deallocate(__new_start, __len);
            __throw_exception_again;
        }
    
        //析构原vector所有元素
        std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
                    _M_get_Tp_allocator());
        //释放原vector内存空间
        _M_deallocate(this->_M_impl._M_start,
                    this->_M_impl._M_end_of_storage,this->_M_impl._M_start);
        //vector内存地址指向新空间
        this->_M_impl._M_start = __new_start;
        this->_M_impl._M_finish = __new_finish;
        this->_M_impl._M_end_of_storage = __new_start + __len;
    }
    }
    
    
    c++
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/1iTbhGAHxUfzqrpcSV7YBMyjZgQa.png)

其中_M_check_len函数的实现如下:

复制代码
    size_type
    _M_check_len(size_type __n, const char* __s) const
    {
    if (max_size() - size() < __n)
        __throw_length_error(__N(__s));
    //如果n小于当前size,内存加倍,否则内存增长n。
    const size_type __len = size() + std::max(size(), __n);
    return (__len < size() || __len > max_size()) ? max_size() : __len;
    }
    
    
    c++
    
    

可以发现vector内存分配策略并不是简单的加倍,如果n小于当前size,内存加倍,否则内存增长n。

接着我们讲一下vector怎么删除元素。vector删除元素的函数有以下几个:

复制代码
    // 删除某个位置上的元素,返回下一个元素的位置
    erase(const_iterator __position)
    // 删除一个区间内的所有元素
    erase(const_iterator __first, const_iterator __last)
    // 清空所有元素
    clear()
    // 借助remove删除与val相等的所有元素
    erase(remove(begin(), end(), val), end());
    // 借助remove_if删除满足某个条件的所有元素
    erase(remove_if(begin(), end(), <lambda>), end());
    
    
    c++
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-17/THsbuQoEVBfxYRwk5ndID3rS1XNl.png)
vector使用陷阱

vector的函数除了at函数之外,都不抛出异常 ,如果要取元素的话,均需要判断是否越界,不然会引发未知的错误;

vector的内存占用空间只增不减,在使用vector时要及时清除元素和回收内存,尤其是在static对象中使用vector保存元素时。vector提供的删除元素的成员函数,只是调用元素的析构函数析构掉元素,已经分配的内存并不会收回;

所有内存空间是在vector析构时候才能被系统回收。empty()用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear(),vector所占用的内存空间依然如故,无法保证内存的回收。

如果需要空间动态缩小,可以考虑使用deque。如果非vector不可,可以用swap()来帮助你释放内存。具体方法如下:

如果vector中存放的是指针,那么当vector销毁的时候,这些指针指向的对象不会被销毁,所占用的内存也不会被释放。

全部评论 (0)

还没有任何评论哟~