Vector的模拟实现

博主的博客主页——>点击这里
博主的gitee主页——>点击这里


前言

上一篇文章我带大家了解了我们STL库中的容器string,这期我们接着来学习下一个容器vector。

接口部分较为简单,大家自行查阅文档即可,本篇文章主要是讲解底层实现原理,可以帮助大家更好的理解。

一、vector文档入口

点击这里——>vector文档

①构造函数

default (1)	explicit vector (const allocator_type& alloc = allocator_type());
fill (2)explicit vector (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());
range (3)template <class InputIterator>vector (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());
copy (4)vector (const vector& x);
int main()
{
   //(1)创建空vector
   vector<int> v;
   //(2)填充构造函数
   vector<int> v(5,3);//每个都是3,即[3,3,3,3,3]
   //(3)范围构造函数
   int arr[] = {1,2,3,4};
   vector<int> v(arr+1,arr+4);//用arr的第二个元素开始到最后一个元素进行初始化
   //(4)拷贝构造函数
   vector<int> v1 = {1,2,3};
   vector<int> v2(v1);
}

②插入数据相关接口(push_back,insert)

void push_back (const value_type& val);
single element (1)iterator insert (iterator position, const value_type& val);
fill (2)void insert (iterator position, size_type n, const value_type& val);
range (3)	template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
int main()
{
    //push_back,只能尾插一个元素
    vector<int> v = {1,2,3};
    v.push_back(4);
    //(1)single element,插入单个元素
    vector<int> v = {1,2,3};
    auto it = v.begin()+1;//it为指向第二个元素的迭代器
    v.insert(it,5);//最终变成了{1,5,2,3};
    //(2)fill,插入多个相同的元素
    vector<int> v = {1,4};
    auto it = v.begin();
    v.insert(it,2,3)//插入两个3,v变为{3,3,1,4}
    //(3)range,迭代器范围插入
    vector<int> v = {1,5};
    int arr[] = {2,3,4};
    auto it = v.begin()+1;//it指向第二个元素
    v.insert(it,arr,arr+3);//在第二个元素的位置插入了234,最终变为{1,2,3,4,5}
}

③删除相关接口(pop_back,erase)

void pop_back();
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
int main()
{
    //push_back
    vector<int> v = {1,2,3};
    v.push_back();//v为{1,2}
    //erase单个元素
    vector<int> v = {1,2,3};
    auto it = v.begin();//it为指向首元素的迭代器
    v.erase(it)//删除首个元素,v为{2,3}
    //erase删除迭代器区间的范围元素(左闭右开)
    vector<int> v = {1,2,3,4,5};
    auto first = begin(),last = begin+4;//first指向首元素,last指向最后一个元素
    v.insert(first,last);//遵循左闭右开的原则,最后v为{5}
}

④容量相关的容器(size,resize,capacity,empty,reserve)

1.size
size_type size() const;
2.resize
void resize (size_type n, value_type val = value_type());
3.capacity
size_type capacity() const;
4.empty
bool empty() const;
5.reserve
void reserve (size_type n);

二、vector的底层结构

vector的底层就是由数据实现的,只不过多了三个指针分别指向首元素(_start)、最后一个元素的下一个位置(——finish)以及指向容量大小的下一个位置(_endof_storeage)。

如图:
在这里插入图片描述

初始框架

那就让我们先来定义一下vector的初始框架,再一步一步补上相关的容器。

template<class T>
class vector
{
public:
    size_t size()const
    {
       return _finish - _start;//指针相减得到得到他们的相对位置,这里得到的是目前元素个数
    }
    size_t capacity()const
    {
       return _endof_stareage - _start;//这里得到目前容量大小
    }
private:
    T* _start;//指向首元素的指针
    T* _finish;//指向最后一个元素的下一个位置的指针
    T* _endof_storeage;//指向容量的下一个位置的指针
};

构造相关逻辑

//拷贝构造
public: 
     vector(const vector<T>& v)
          :_start(nullptr)
          ,_finish(nullptr)
          ,_endof_storeage(nullptr)
     {
          for(auto e& :v)
          {
              push_back(e);
          }
     }
//迭代器区间构造
template<class InputIterator>
vector(InputIterator first,InputIterator last)
{
     while(first != last)
     {
         push_back(*first);
     }
}
//Initialzar_list构造
vector(initialzar_list<T>& v)
{
    for(auto e& : v)
    {
        push_back(e);
    }
}

扩容以及插入操作

这里需要注意我们需要扩容的条件是什么,当_finsih == _endof_storeage时,就需要进行扩容

class vector
{
public:
    void reserve(n)
    {
        size_t old_size = size();
        if(n > capacity())
        {
             T* tmp = new T[n];
             if(_start)//如果_start不为空,也就是非初始化的时候
             {
                 memcpy(tmp,_start,sizeof(T)*size());//memcpy是按字节进行拷贝,将原先的数据拷贝到新创建的数组空间中。
                 delete[] _start;//释放旧空间的内容
             }
             _start = tmp;
             _finish = _start + old_size;//不可写为_statr+size(),size()是_finsih - _start,_start更新后指向了新空间,而_finsih还指向着旧空间,无相减的意义。
             _endof_storeage = _start + n;
        }
    }
    void push_back(const T& x)
    {
        if(_finish == _endof_storeage)
        {
            reserve(capacity() == 0? 4 : 2 * capacity());
        }
        //走到这里。扩容完毕
        (*_finish) = x;
        _finish++;
    }
};

在这里插入图片描述

insert与erase

这两者都是相当重要的接口,因为他们的操作会涉及到一个非常重要的知识点——迭代器失效

什么是迭代器失效

举个直观的例子:你手里拿着“指向容器第3个元素”的迭代器,结果容器因为插入/删除元素重新分配了内存、或者元素顺序被打乱,这时候你再用这个旧迭代器去访问元素,要么拿到错误的数据,要么直接程序崩溃——这就是“迭代器失效”的本质:迭代器原本指向的“位置/状态”,因为容器的修改变得无效了。
在这里插入图片描述

那么,迭代器失效有什么影响

使用失效的迭代器访问容器元素时,会触发内存访问违规,直接导致程序崩溃,所以迭代器失效是绝对不可取的。

解决方法:

insert与erase操作后,为避免迭代器失效,他俩会更新迭代器。例如insert,会返回指向被插入元素的迭代器(例如我们想插入5,返回的是指向5的新迭代器);erase则会返回迭代器指向原本元素的下一个元素(12345我们想删去3,则返回的是1245指向4的迭代器)

class vector
{
public:
     iterator insert(iterator pos,const T&x)
     {
         if(_finish == _endof_storeage)
         {
             size_t len = pos - _start;
             reserve(capacity == 0?4:2*capacity());
             pos = _start + len;//这里进行了扩容操作,_start已经进行了更新,如果我们不这么做,pos还是指向原来的旧空间。
         }
         iterator end = _finish;
         while(end >= pos)
         {
             *end = *(end - 1);
             end--;             
         }
         *pos = x;//迭代器失效:指向的内存废了,这里只是合法中的内存数据被修改
         ++_finish;
         return pos;
     }
     iterator erase(iterator pos)
     {
         iterator end = pos;
         while(end+1<_finish)
         {
            *end = *(end+1);
             end++;
         }
         --_finish;
         return pos;
     }
};

总结

本文主要针对vector作了一个大致的讲解,对一些重要的接口进行了细致的解答,还需大家下功夫去练习与理解。

引用和提供了关于实现vector的两种方法。其中,引用展示了一个使用reserve和push_back方法的示例,而引用展示了一个使用new和memcpy函数的示例。这两种方法都是常见的实现vector的方式。 在第一种方法中,通过reserve函数可以预留足够的内存空间,然后使用push_back函数逐个将元素添加到vector中。这种方法的好处是可以避免不必要的内存重分配,提高了效率。 而第二种方法使用new操作符在堆上分配内存空间,并使用memcpy函数将已有的vector对象的数据复制到新的内存空间中。通过这种方式,可以实现深拷贝,即两个vector对象拥有独立的内存空间。这种方法的好处是可以在不修改原始vector对象的情况下创建一个新的vector对象。 除了以上两种方法,还可以使用其他方式实现vector类。例如,可以使用动态数组来实现vector的底层数据结构,然后通过成员函数实现vector的各种操作,如增加、删除、查找等。 总结来说,c语言模拟实现vector的关键是动态内存管理和对元素的增删改查操作。可以使用预留空间和逐个添加元素的方式,也可以使用动态数组和复制数据的方式来实现vector类。具体的实现方式可以根据需求和实际情况选择。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C++——vector模拟实现](https://blog.youkuaiyun.com/weixin_49449676/article/details/126813526)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值