从零开始写STL—容器—vector

Posted joeylee97

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始写STL—容器—vector相关的知识,希望对你有一定的参考价值。

从0开始写STL—容器—vector

vector又称为动态数组,那么动态体现在哪里?vector和一般的数组又有什么区别?vector中各个函数的实现原理是怎样的,我们怎样使用会更高效?

  • 以上内容我们将通过写一个自己的vector来进行学习

typedef 简析

在容器类的最前面我们会看到许多的typedef ,常见的如下:

    public:
        typedef T value_type;
        typedef value_type* pointer;
        typedef value_type* iterator;
        typedef T& reference;
        typedef size_t size_type;
        typedef ptrdiff_t difference_type;

这些typedef 的意义在于:

  • 1.使得变量名便于理解和修改
  • 2.提供RTTI的实现可能性,通过模板萃取可以萃取出 value_type 等

Alloctor 简析

在这里我们只需要知道allocator类是vector类和底层内存之间的一个中间层,屏蔽了内存分配,对象初始化的细节。

内存分配相关函数->动态分配内存

首先vector和普通数组一样,是在内存中连续分布的,然而它可以实现内存的动态分配(动态分配是指vector可以自动扩充自己的容量来满足存储元素的需要)。
现有的申请到的内存空间不足,会自动申请重新分配一块更大的内存(也就是会自动进行一次reserve函数,在push_back插入元素的时候,为了避免大量重新分配浪费性能,会将内存分配到原来的两倍)

该内存模型的实现形式

vector内部会维护三个指针,start 指针指向vector中的第一个元素,end指针指向vector中最后一个元素的后面,end_of_storage 申请到内存的最末端。

destroyanddeallocate函数

  • 如名称所示,这个函数可以销毁当前所有元素并且回收内存。析构函数直接调用该函数
void destroyanddeallocate()
{
    if(capacity()==0)
    {
        return ;
    }
    for(auto it = start; it != End; it++)
    {
        destroy(it);//对所有元素调用析构函数
    }
    data_allocator.deallocate(start,end_of_storage-start);//由allocator回收内存
}
~vector()
{
    destroyanddeallocate();
}

reallocate 函数 重新分配内存

  • 函数负责在内存不够的情况下动态分配内存并且将原有元素拷贝到新的内存中,通过将默认变量设置为0,如果没有传入变量覆盖掉默认变量就将申请的内存扩大到原有内存的两倍。
void reallocate(size_type new_size = 0)
{
    if(new_size == 0)
    {
        new_size = size() == 0 ? 1 : 2 * size();
    }
    iterator new_start = data_allocator.allocate(new_size);//首先分配一块新的内存
    if(new_start == nullptr)
    {
        //分配失败的处理
        //略
    }
    iterator new_End = ministl::uninitialized_copy(start, End, new_start);//移动元素
    DestroyAndDeallocateAll();//销毁元素 并且 回收内存
    start = new_start;
    End = new_End;
    end_of_storage = start + new_size;// 设置指针位置
}

构造函数

        vector()
        {
            End = end_of_storage = start = nullptr;
        }
        vector(size_t n, value_type val = value_type())//要求value_type 有一个默认构造函数
        {
            start = data_allocator.allocate(n);
            End = end_of_storage = start + n;
            ministl::uninitialized_fill_n(start, n, val);// 使用默认构造函数的值进行拷贝构造
        }
        vector(iterator first, iterator last)
        {
            start = data_allocator.allocate(last - first);
            End = end_of_storage = uninitialized_copy(first, last, start);
        }
        vector(const vector<T, Alloc> &rhs)
        {
            assign(rhs.start, rhs.End);
        }
        vector(vector<T, Alloc> &&rhs)
        {
            if (this != &rhs)
            {
                DestroyAndDeallocateAll();
                start = rhs.start, End = rhs.End, end_of_storage = rhs.end_of_storage;
                rhs.start = rhs.end_of_storage = rhs.End = nullptr;//将移动后的右值置于可析构的状态
            }
        }

Element Access 相关函数

  • 通过指针的移动获得元素的值,注意传递引用 这样外部的修改才是对容器内部元素的修改。
        value_type& operator [](size_type n)
        {
            if (n >= size())
            {
                std::cerr << "Out of range" << std::endl;
                std::exit(1);
            }
            return *(start + n);
        }
        value_type& front()
        {
            if (empty())
            {
                std::cerr << "front on empty vector" << std::endl;
                std::exit(1);
            }
            return *(start);
        }
        value_type& back()
        {
            if (empty())
            {
                std::cerr << "back on empty vector" << std::endl;
                std::exit(1);
            }
            return *(End - 1);
        }

Modify 修改相关函数

  • 内容较多 见注释
        void resize(size_type new_size, value_type val = value_type())
        {   
            if (new_size < size())// 无需重新分配内存
            {
                // 销毁不需要的元素,无需回收内存
                for (iterator it = start + new_size; it != End; it++)
                {
                    destroy(it);
                }
                End = start + new_size;// 重新设置最后一个元素位置
            }
            else if (new_size < capacity())//小于申请到的内存容量
            {
                //只需将后面未初始化的内存初始化一下
                uninitialized_fill_n(End, new_size - size(), val);
                End = start + new_size;
            }
            else // 重新分配内存
            {
                reallocate(new_size);
                uninitialized_fill_n(End, new_size - size(), val);
                End = start + new_size;
            }
        }
        void reserve(size_type n)
        {
            if (n <= capacity()) return;
                reallocate(n); // 重新分配内存
        }
        bool empty()
        {
            return start == End;
        }
        //Modifiers
        void push_back(value_type data)
        {
            if (capacity() == size())//判断容量
                reallocate();
            construct(End++, data);//更改End位置并进行初始化
        }
        void pop_back()
        {
            if (empty())
            {
                std::cerr << "pop on empty" << std::endl;
                std::exit(1);
            }
            End--;
            destroy(End);//析构
        }
        template<class InputIterator>
        void assign(InputIterator first, InputIterator last)
        {
            DestroyAndDeallocateAll();
            start = data_allocator.allocate(last - first);//申请内存
            End = end_of_storage = uninitialized_copy(first, last, start);//初始化
        }
        void assign(size_type n, const value_type &v)
        {
            DestroyAndDeallocateAll();
            start = data_allocator.allocate(n);
            End = end_of_storage = start + n;
            uninitialized_fill_n(start, n, v);
        }
        iterator insert(iterator pos, const value_type &v)
        {
            if (pos == end())//特殊情况
            {
                push_back(v);
                return &back();
            }
            if (size() == capacity())//内存申请
                reallocate();
            iterator new_end = End + 1;
            data_allocator.construct(new_end, v);//要进行初始化否则会报错
            copy_backward(pos, End, new_end);//移动pos后面的元素到最终位置
            *pos = v;//赋值
            End = new_end;
            end_of_storage++;
            return pos;
        }
        /*
        这个函数比较复杂,一定注意End pos 的含义变化
        */
        iterator insert(iterator pos, const size_type &n, const value_type &val)
        {
            size_type index = pos - start;
            if (n + size() > capacity())//判断内存容量
                reserve(n + size());
            pos = start + index;// 插入元素的位置
            iterator new_end = End + n; // 元素插入后 结束点的位置
            size_type after_num = End - pos;// 插入点后面的元素个数
            if (after_num > n) // 如果后面元素个数比要插入的元素多 
            {
                    uninitialized_copy(End - n, End, End);// 将End-n 到 End 的元素放置到最正确位置
                End += n; // 重新设置End 的位置
                copy_backward(pos, End - n, End);// 将pos到End-n的元素放置正确位置
                fill(pos, pos + n, val);// 填充出去
                return pos + n;// 返回插入最后一个元素的位置
            }
            else //插入元素个数很多
            {
                iterator old_end = End; 
                uninitialized_fill_n(End, n - after_num, val);//填充n-afternum个元素,后面的元素没必要初始化因为后面的元素将会是afternum个插入节点后面的元素
                End += n - after_num;
                uninitialized_copy(pos, old_end, End);// 拷贝afternum个插入节点后面的元素
                End += after_num;
                fill(pos, old_end, val);// 填充
                return pos + n;
            }
        }
        /*
        这个和上面大同小异
        */
        template<class InputIterator>
        void insert(iterator pos, InputIterator first, InputIterator last)
        {
            size_type add_num = last - first, index = pos - start, after_num = End - pos;
            if (add_num + size() > capacity())
                reserve(add_num + size());
            pos = start + index;
            if (pos > End) pos = End;
            if (pos <= End - add_num)
            {
                uninitialized_copy(End - add_num, End, End);
                copy_backward(pos, End - add_num, End);
                copy(first, last, pos);
                End += add_num;
            }
            else
            {
                iterator new_end = End + add_num;
                uninitialized_copy(pos, End, new_end - after_num);
                copy_n(first, after_num, pos);
                uninitialized_copy_n(first + after_num, add_num - after_num, End);
                End += add_num;
            }
        }
        iterator erase(iterator pos)
        {
            copy(pos + 1, End, pos);
            destroy(End - 1);
            End--;
            return pos;
        }

        iterator erase(iterator first, iterator last)
        {
            copy(last, End, first);
            for (auto it = End - (last - first); it != End; it++)
                destroy(it);
            End = End - (last - first);
            return last;
        }
        void clear()
        {
            DestroyAndDeallocateAll();
            End = start;
        }
        void swap(vector<T, Alloc> &rhs)
        {
            iterator tmp;
            tmp = start, start = rhs.start, rhs.start = tmp;
            tmp = End, End = rhs.End, rhs.End = tmp;
            tmp = end_of_storage, end_of_storage = rhs.end_of_storage, rhs.end_of_storage = tmp;
        }
        bool operator==(vector<T, Alloc>& rhs)
        {
            if (size() != rhs.size())//首先size 要相同
                return false;
            else
            {
                auto it = begin();
                auto j = rhs.begin();
                for (; it != end(); )
                {
                    if (*it != *j)
                        return false;
                    else
                        it++, j++;
                }
                return true;
            }
        }
        bool operator!=(vector<T, Alloc>& rhs)
        {
            return !(*this == rhs);
        }
        void operator=(const vector<T, Alloc>& rhs)
        {
            if (this != &rhs)
            {
                assign(rhs.begin(), rhs.end());
                /*vector<T, Alloc> tmp(rhs.begin(), rhs.end());
                ministl::swap(*this, rhs);*/
            }
        }
    };
    
    template<class T>
    void swap(vector<T>& a, vector<T>& b)
    {
        a.swap(b);
    }

怎样高效使用

  • 尽量避免vector容器前端的插入,会引起大量元素的重新拷贝
  • reserve 内存会一定情况下减少内存的重新分配

以上是关于从零开始写STL—容器—vector的主要内容,如果未能解决你的问题,请参考以下文章

从零开始学c++之STL——vector的介绍和使用

从零开始写STL - 智能指针

从零开始写STL—functional

从零开始写STL-内存部分-内存分配器allocator

c++ 容器含义

STL源代码剖析 容器 stl_vector.h