C++模拟实现vector流程详解

 更新时间:2022年08月08日 15:46:29   作者:恋恋风辰  
这篇文章主要介绍了C++容器Vector的模拟实现,Vector是一个能够存放任意类型的动态数组,有点类似数组,是一个连续地址空间,下文更多详细内容的介绍,需要的小伙伴可以参考一下

模拟vector

我们可以通过模板实现类似vector的类。我们实现一个StrVecTemp类,其内部通过allocator开辟空间,存储的类型用T来表示,T是模板类型。

template <typename T>
class StrVecTemp
{
public:
    StrVecTemp() : elements(nullptr), first_free(nullptr),
                   cap(nullptr) {}
    //拷贝构造函数
    StrVecTemp(const StrVecTemp &);
    //拷贝赋值运算符
    StrVecTemp &operator=(const StrVecTemp &);
    //移动构造函数
    StrVecTemp(StrVecTemp &&src) noexcept : elements(src.elements),
                                            first_free(src.first_free), cap(src.cap)
    {
        //将源数据置空
        src.elements = src.first_free = src.cap = nullptr;
    }
    template <class... Args>
    void emplace_back(Args &&...args);
    //析构函数
    ~StrVecTemp();
    //拷贝元素
    void push_back(const T &);
    //抛出元素
    void pop_back(T &s);
    //返回元素个数
    size_t size() const { return first_free - elements; }
    //返回capacity返回容量
    size_t capacity() const { return cap - elements; }
    //返回首元素的指针
    T *begin() const
    {
        return elements;
    }
    //返回第一个空闲元素指针
    T *end() const
    {
        return first_free;
    }
private:
    //判断容量不足靠皮新空间
    void chk_n_alloc()
    {
        if (size() == capacity())
        {
            reallocate();
        }
    }
    //重新开辟空间
    void reallocate();
    // copy指定范围的元素到新的内存中
    std::pair<T *, T *> alloc_n_copy(const T *, const T *);
    //释放空间
    void free();
    //数组首元素的指针
    T *elements;
    //指向数组第一个空闲元素的指针
    T *first_free;
    //指向数组尾后位置的指针
    T *cap;
    //初始化alloc用来分配空间
    static std::allocator<T> alloc;
};
template <typename T>
std::allocator<T> StrVecTemp<T>::alloc;

alloc在使用前要在类外初始化,因为是模板类,所以放在.h中初始化即可。

接下来我们要实现根据迭代器开始和结束的区间copy旧元素到新的空间里

//实现区间copy
template <typename T>
std::pair<T *, T *> StrVecTemp<T>::alloc_n_copy(const T *b, const T *e)
{
    auto newdata = alloc.allocate(e - b);
    //用旧的数据初始化新的空间
    auto first_free = uninitialized_copy(b, e, newdata);
    return {newdata, first_free};
}

实现copy构造

//实现拷贝构造函数
template <class T>
StrVecTemp<T>::StrVecTemp(const StrVecTemp &strVec)
{
    auto rsp = alloc_n_copy(strVec.begin(), strVec.end());
    //利用pair类型更新elements, cap, first_free
    elements = rsp.first;
    first_free = rsp.second;
    cap = rsp.second;
}

实现copy赋值

//拷贝赋值运算符
template <class T>
StrVecTemp<T> &StrVecTemp<T>::operator=(const StrVecTemp &strVec)
{
    if (this == &strVec)
    {
        return *this;
    }
    //如果不是自赋值,就将形参copy给自己
    auto rsp = alloc_n_copy(strVec.begin(), strVec.end());
    elements = rsp.first;
    first_free = rsp.second;
    cap = rsp.second;
}

析构函数要先销毁数据再回收内存

//析构函数
template <class T>
StrVecTemp<T>::~StrVecTemp()
{
    //判断elements是否为空
    if (elements == nullptr)
    {
        return;
    }
    //缓存第一个有效元素的地址
    auto dest = elements;
    //循环析构
    for (size_t i = 0; i < size(); i++)
    {
        //析构每一个元素
        alloc.destroy(dest++);
    }
    //再回收内存
    alloc.deallocate(elements, cap - elements);
    elements = nullptr;
    cap = nullptr;
    first_free = nullptr;
}

重新开辟空间

template <class T>
void StrVecTemp<T>::reallocate()
{
    T *newdata = nullptr;
    //数组为空的情况
    if (elements == nullptr || cap == nullptr || first_free == nullptr)
    {
        newdata = alloc.allocate(1);
        elements = newdata;
        first_free = newdata;
        // cap指向数组尾元素的下一个位置
        cap = newdata + 1;
        return;
    }
    //原数据不为空,则扩充size两倍大小
    newdata = alloc.allocate(size() * 2);
    //新内存空闲位置
    auto dest = newdata;
    //就内存的有效位置
    auto src = elements;
    //通过移动操作将旧数据放到新内存中
    for (size_t i = 0; i != size(); ++i)
    {
        alloc.construct(dest++, std::move(*src++));
    }
    //移动完旧数据后一定要删除
    free();
    //更新数据位置
    elements = newdata;
    first_free = dest;
    cap = newdata + size() * 2;
}

上面的函数用到了free函数,我们自己实现一个free

template <typename T>
void StrVecTemp<T>::free()
{
    //先判断elements是否为空
    if (elements == nullptr)
    {
        return;
    }
    auto dest = elements;
    //遍历析构每一个对象
    for (size_t i = 0; i < size(); i++)
    {
        // destroy 会析构每一个元素
        alloc.destroy(dest++);
    }
    //再整体回收内存
    alloc.deallocate(elements, cap - elements);
    elements = nullptr;
    cap = nullptr;
    first_free = nullptr;
}

压入元素和弹出元素

//拷贝元素
template <class T>
void StrVecTemp<T>::push_back(const T &t)
{
    chk_n_alloc();
    alloc.construct(first_free++, t);
}
//抛出元素
template <class T>
void StrVecTemp<T>::pop_back(T &s)
{
    //先判断是否为空
    if (first_free == nullptr)
    {
        return;
    }
    //判断size为1
    if (size() == 1)
    {
        s = *elements;
        alloc.destroy(elements);
        first_free = nullptr;
        elements = nullptr;
        return;
    }
    s = *(--first_free);
    alloc.destroy(first_free);
}

接下来要实现emplace_back,因为emplace_back支持多种构造函数的参数,所以要用模板参数列表的方式定义该函数。

模板参数列表和形参列表都要用参数包的方式

template <class T>
template <class... Args>
void StrVecTemp<T>::emplace_back(Args &&...args)
{
    chk_n_alloc();
    alloc.construct(first_free++, forward<Args>(args)...);
}

Args是模板参数包,args是参数列表。因为construct的参数可能为右值引用,所以要用forward将原参数列表类型原样转发。

// forward既扩展了模板参数包Args,又扩展了函数参数包args
// std::forward<Args>(args)... 等价于std::forward<Ti>(ti)
//比如传递给emplace_back(10,'c');
//相当于调用 alloc.construct(first_free++, forward<int>(10), forward<char>('c'))
//调用的就是插入cccccccccc

总结

本文模拟实现了vector的功能。

视频链接

源码链接

到此这篇关于C++模拟实现vector流程详解的文章就介绍到这了,更多相关C++ vector内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • OpenCV实现霍夫变换直线检测

    OpenCV实现霍夫变换直线检测

    这篇文章主要为大家详细介绍了OpenCV实现霍夫变换直线检测,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • C++实现循环队列和链式队列的示例

    C++实现循环队列和链式队列的示例

    下面小编就为大家分享一篇C++实现循环队列和链式队列的示例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-12-12
  • C++中new的越界访问问题

    C++中new的越界访问问题

    越界访问指访问了不是程序申请的内存区域,比如申请了5个字节的char数组,结果读写数据的第六个元素,或者访问了释放后的内存等等。
    2016-04-04
  • C语言高斯消元法的使用详解

    C语言高斯消元法的使用详解

    本篇文章是对C语言中高斯消元法的使用进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C++中内存池的简单原理及实现详解

    C++中内存池的简单原理及实现详解

    内存池的思想是,在真正使用内存之前,预先申请分配一定数量、大小预设的内存块留作备用。本文主要来和大家聊聊内存池的简单原理及实现,希望对大家有所帮助
    2023-03-03
  • C++ Sqlite3的使用方法

    C++ Sqlite3的使用方法

    这篇文章主要介绍了C++ Sqlite3的使用方法,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • 浅谈单调队列、单调栈

    浅谈单调队列、单调栈

    其实,单调队列和单调栈是类似的,在我看来,这两个东西只是名字不一样 - - ! 比较容易想的一道题啦! 首先,这题的两个关键点: 1、区间的和。这个简单,地球人都知道! 2、区间的最小值。
    2015-07-07
  • Qt/C++编写视频监控系统之自定义音柱显示功能

    Qt/C++编写视频监控系统之自定义音柱显示功能

    通过音柱控件实时展示当前播放的声音产生的振幅的大小,得益于音频播放组件内置了音频振幅的计算,可以动态开启和关闭,开启后会对发送过来的要播放的声音数据,这篇文章主要介绍了Qt/C++编写视频监控系统之自定义音柱显示功能,需要的朋友可以参考下
    2024-01-01
  • C语言库函数strcpy的使用及模拟实现

    C语言库函数strcpy的使用及模拟实现

    本文主要介绍了C语言库函数strcpy的使用及模拟实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-04-04
  • 详解如何用c++实现平衡二叉树

    详解如何用c++实现平衡二叉树

    平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),由前苏联的数学家Adelse-Velskil和Landis在1962年提出的高度平衡的二叉树,根据科学家的英文名也称为AVL树。本文介绍了它的原理和如何用C++代码来实现
    2021-06-06

最新评论