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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ vector容器实现贪吃蛇小游戏

    本文实例为大家分享了C++ vector容器 实现贪吃蛇,供大家参考,具体内容如下 使用vector容器实现贪吃蛇简化了很多繁琐操作,且相比之前我的代码已经做到了尽量的简洁 技术环节: 编译环境:windows VS2019 需求: 控制贪吃蛇吃食物,吃到一个食物蛇身变长一节,得分增加,撞墙或撞自己则游戏结束. 思路: 创建一个vector容器,容器内存储蛇的每节身体的结构变量,结构变量中保存蛇身体的xy坐标,通过使用vector成员方法不断添加和删除容器中的数据,实现蛇坐标的规律移动,吃到食物

  • C++示例讲解vector容器

    目录 vector基本概念 创建 vector 的各种方法 vector容器的构造 vector赋值操作 vector容量和大小 vector容器插入和删除 vector数据存取 vector互换容器 vector基本概念 功能: vector数据结构和数组非常相似,也称为单端数组 vector与普通数组区别: 不同之处在于数组是静态空间,而vector可以动态扩展 动态扩展: 并不是在原空间之后继续接新空间,而是找更大的内存空间,然后将元数据拷贝新空间,释放原空间 创建 vector 的各种方

  • C++ 容器 Vector 的使用方法

    目录 Vector简介 Vector 与数组 创建 vector 的各种方法 访问 vector 的元素 删除元素 前言: 我们都是带着问题学习,假设一个任务,也可以理解为一个问题,通过找解决方案来提升自己 c++ 的编程能力,尝试这是否一条好的路线,希望找到这种学习方式有效 问题简单描述一下,就是从以字符串,课程名称组成的集合中将一些包含特定文字,将其从列表中删除 Vector简介 Vector 是一个能够存放任意类型的动态数组,有点类似数组,是一个连续地址空间, Vector 与数组 Vec

  • C++Vector容器常用函数接口详解

    目录 一.基础框架 二.迭代器实现 三.size capacity resize reserve 四.insert,erase 五.pop_back,push_back 六.operator[] 七.构造函数 析构函数 赋值重载 一.基础框架 template<class T> class vector { public: typedef T* iterator; typedef const T* const_iterator; private: iterator _start;//指向第一个

  • C++中vector容器的注意事项总结

    目录 容量(capacity)和大小(size)的区别 容器扩容的本质 emplace_back()和push_back()的区别 emplace()和insert()的区别 附:如果vector是空的,并且没有分配空间,切忌用下标进行访问,会出错!!! 总结 容量(capacity)和大小(size)的区别 vector 容器的容量(用 capacity 表示),指的是在不分配更多内存的情况下,容器可以保存的最多元素个数:而 vector 容器的大小(用 size 表示),指的是它实际所包含的

  • C++ STL中vector容器的使用

    目录 一.vector (1)区分size()和capacity() (2)迭代器失效 (3)区分const_iterator和constiterator (4)区分reserve()和resize() (5)push_back和emplace (6)关于原位构造(定位new+完美转发) 总结 一.vector (1)区分size()和capacity() size():返回容纳的元素个数 capacity():返回当前分配存储的容量 (2)迭代器失效 (3)区分const_iterator和c

  • C++类模板实战之vector容器的实现

    目录 案例要求 完成步骤 1.封装数组类属性并完成有参构造以及析构函数 2.提供对应的深拷贝构造函数防止调用析构时出错 3.重载类内的赋值运算符防止浅拷贝问题出现 4.提供尾部插入和删除的方法 5.重载[]得到数组中对应下标的数据信息 6.提供get方法获取当前数组容量及大小 7.提供打印函数测试基本数据类型和自定义数据类型的存储 案例要求 可以对内置数据类型以及自定义数据类型的数据进行存储 将数组中的数据存储到堆区 构造函数中可以传入数组的容量 提供对应的拷贝构造函数以及operator=防止

  • C++之vector容器的的声明初始化和增删改查

    C++vector容器 C++中有两种类型的容器:顺序容器和关联容器. 顺序容器主要有vector.list.deque等.其中vector表示一段连续的内存,基于数组实现,list表示非连续的内存,基于链表实现,deque与vector类似,但是对首元素提供插入和删除的双向支持. 关联容器主要有map和set.map是key-value形式,set是单值.map和set只能存放唯一的key,multimap和multiset可以存放多个相同的key. 容器类自动申请和释放内存,因此无需new和

  • 详解C++ STL vector容器访问元素的几种方式

    学会如何创建并初始化 vector 容器之后,本节继续来学习如何获取(甚至修改)容器中存储的元素. 访问vector容器中单个元素 首先,vector 容器可以向普通数组那样访问存储的元素,甚至对指定下标处的元素进行修改,比如: #include <iostream> #include <vector> using namespace std; int main() { vector<int> values{1,2,3,4,5}; //获取容器中首个元素 cout &l

  • 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 St

  • Vue echarts模拟后端数据流程详解

    目录 KOA2的使用 安装 Koa app.js入口文件 KOA2的使用 KOA2是由Express 原班人马打造. 环境依赖 Node v7.6.0 及以上. 支持 async 和 await 洋葱模型的中间件 写响应函数(中间件) 响应函数是通过use的方式才能产生效果, 这个函数有两个参数, ctx :上下文, 指的是请求所处于的Web容器,我们可以通过 ctx.request 拿到请求对象, 也可 以通过 ctx.response 拿到响应对象 next :内层中间件执行的入口 模拟服务

  • C语言 数据结构之数组模拟实现顺序表流程详解

    目录 线性表和顺序表 线性表 顺序表 静态顺序表 动态顺序表 代码已经放在Gitee上,需要可以小伙伴可以去看看 用C语言数组模拟实现顺序表 Gitee 线性表和顺序表 线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列,这是我们广泛使用的数据结构. 线性表在逻辑上是线性结构,也就说是连续的一条直线.但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储. 常见的线性表:顺序表.链表.栈.队列.字符串- 顺序表 顺序表是用一段物理地址连

  • C++ String部分成员模拟实现流程详解

    目录 string类的成员设计 普通构造函数的模拟 拷贝构造函数的模拟 赋值重载函数的模拟 String的析构函数模拟 补全上述的成员函数 迭代器的简单模拟 其他成员函数的模拟 string类的成员设计 class string { private: char* _str; int _size; int _capacity; }; 说明:以下的五个成员函数的模拟实现,均去除了_size 和_capacity成员变量,目的是为了更方便解释重点.在五个成员函数模拟后,会对string类的设计进行补全

  • Android zygote启动流程详解

    对zygote的理解 在Android系统中,zygote是一个native进程,是所有应用进程的父进程.而zygote则是Linux系统用户空间的第一个进程--init进程,通过fork的方式创建并启动的. 作用 zygote进程在启动时,会创建一个Dalvik虚拟机实例,每次孵化新的应用进程时,都会将这个Dalvik虚拟机实例复制到新的应用程序进程里面,从而使得每个应用程序进程都有一个独立的Dalvik虚拟机实例. zygote进程的主要作用有两个: 启动SystemServer. 孵化应用

  • php操作ElasticSearch搜索引擎流程详解

    目录 一.安装 二.使用 三.新建ES数据库 四.创建表 五.插入数据 六. 查询所有数据 七.查询单条数据 八.搜索 九.测试代码 〝 古人学问遗无力,少壮功夫老始成 〞 如果这篇文章能给你带来一点帮助,希望给飞兔小哥哥一键三连,表示支持,谢谢各位小伙伴们. 一.安装 通过composer安装 composer require 'elasticsearch/elasticsearch' 二.使用 创建ES类 <?php require 'vendor/autoload.php'; //如果未设

  • jmeter接口测试教程及接口测试流程详解(全网仅有)

    目录 一.Jmeter简介 二.Jmeter安装 三.设置Jmeter语言为中文环境 四.Jmeter主要元件 五.Jmeter元件的作用域和执行顺序 六.Jmeter进行接口测试流程 七.Jmeter进行接口测试流程步骤详解 八.Jmeter接口测试必定用到的扩展阅读 一.Jmeter简介 Jmeter是由Apache公司开发的一个纯Java的开源项目,即可以用于做接口测试也可以用于做性能测试. Jmeter具备高移植性,可以实现跨平台运行. Jmeter可以实现分布式负载. Jmeter采用

  • C++模拟实现List迭代器详解

    目录 概念 迭代器使用 迭代器模拟实现 迭代器的大体结构 构造函数 解引用重载 重载 自增实现 自减实现 运算符重载 迭代器失效 模拟List 概念 迭代器是一种抽象的设计概念,其定义为:提供一种方法,使他能够按顺序遍历某个聚合体(容器)所包含的所有元素,但又不需要暴露该容器的内部表现方式. 迭代器是一种行为类似智能指针的对象, 而指针最常见的行为就是内 容提领和成员 访问. 因此迭代器最重要的行为就是对operator*和operator->进行重载. STL的中心思想在于: 将数据容器和算法

  • Java GUI图形界面开发实现小型计算器流程详解

    目录 一.设计目标 二.界面设计 三.功能实现 四.全部代码 五.功能测试 六.小结 一.设计目标 (1)主要功能:实现简单的加.减.乘.除等双目运算,和开平方.百分数等单目运算 (2)辅助功能:按钮“C”实现清空文本框:按钮“←”实现退格,删除文本框中最右边的一个字符 二.界面设计 创建“面板对象”,并设置其布局管理方式为5行4列的GridLayout布局方式,以用于容纳20个按钮.文本框和容纳20个按钮组件的面板则使用边界布局方式分别将其布局到窗体BorderLayout.NORTH和中央位

  • Vue登录功能的实现流程详解

    目录 Vue项目中实现登录大致思路 安装插件 创建store 封装axios qs vue 插件 api.js的作用 路由拦截 登录页面实际使用 Vue项目中实现登录大致思路 1.第一次登录的时候,前端调后端的登陆接口,发送用户名和密码 2.后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token 3.前端拿到token,将token存储到localStorage和vuex中,并跳转路由页面 4.前端每次跳转路由,就判断 localStroage 中有无 token ,没有就跳转到登

随机推荐