实现一个自定义STL类模版DataStorage

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
参考:https://tool.oschina.net/uploads/apidocs/cpp/en/cpp/container/vector.html
参考:https://tool.oschina.net/uploads/apidocs/cpp/en/cpp/iterator/reverse_iterator.html
参考:https://tool.oschina.net/uploads/apidocs/cpp/en/cpp/concept/RandomAccessIterator.html

引言

C++代码中,STL库的使用,让我们对基本的数据结构使用很便利;

常用的 std::vector数组、std::list链表、std::map映射、std::set集合、std::basic_string字符串等。
这些数据结构的定义我们一般来说,也都是比较清楚的:
vector提供一段连续内存存储,
list提供链表结构存储,
map提供树形结构存储,
set提供树形的结构存储,
basic_string提供连续内存段存储字符。

数据结构清楚,对于stl库的实现呢,或许有时我们觉得比较简单,觉得也就那样,但真实实现起来,要做的工作也还是比较多的。

1. vector的实现分析

拿std::vector来说,我们看看它要实现方法的哪些:

  • 对元素的访问,提供了at, operaotr[], iter等多种数据的访问方式,也提供了front, back等首位元素的访问方式;
  • Iter方式提供了Iterator, reverse_iterator, const_iterator, const_reverse_iterator四种迭代器,分别映射不同的场景需要。iterator要支持多种operator操作,包括operator++, operator–, operator->, operator*等操作,还有operator==, operator!=判定操作。
  • 构造函数要支持空置,初始复制,copy构造,operator=赋值,assign赋值;另外支持std::move右值引用的方式的copy构造,operator==赋值也要来一套。
  • 数据的操作上,push_back, emplace_back, clear都要用,一方面也有std::move右值引用的处理;
  • 数据的复杂操作上,insert,erase操作其实不算很特殊,对于链表结构比较简单,但对于一维整块内存的vector来说,就复杂一些了,从中间插入或删除一条数据时,涉及的后续所有元素的位移。
  • 内存维护上,size增长时,capacity空间不够了,需要新申请内存块,然后把原数据copy到新块上,释放掉老的块;size减少时,如果要回收内存shrink_to_fit回收内存,申请size块,把数据copy过来,释放掉老的块。

2. 定义一个新的类模版DataStorage

下面尝试自己来实现一个模版类,来满足一些常用语法、常用接口的使用。
定义一个datastorage类,综合std::vector与std::array特点,提供一个由这两个类对象组合出来的内存结构类模版,该模版类有一个固定内存块和一个动态内存块,对外提供stl的相关访问方法。

2.1 初始结构

初始结构形如,在DataStorage中,同时拥有两个存储的变量和一个记录长度的变量;
一个变量是array_,拥有固定长度大小的数组内存块;
一个变量是vector_,可以拥有弹性的数据长度;
一个变量是size_,记录当前使用的数据长度;

template <typename T, std::size_t ArraySize>
class DataStorage {
public:// 构造函数DataStorage() : array_(), vector_(), size_(0) {}private:std::array<T, ArraySize> array_;std::vector<T> vector_;size_t size_;
};

2.2 增加访问接口:添加数据与获取数据

增加访问接口:获取数据,添加数据接口。
添加一个push_back方法,往空间里放置数据,放置时,先看看array够不够放,够放时,先往array中放数据;array满了之后,往vector中放数据;
添加一个get方法,基于要get的index位置,或是从array中取数据,或是从vector中取数据,对外提供引用,支持外部修改获取到的数据;
添加了一个operator[]的便利访问方法,使用[]方式访问数据,读取对应index数据或是修改对应index的数据。
添加size(), empty()来获取元素个数和判断。

    // modifyvoid push_back(const T& value){if (size_ < array_.size())array_[size_] = value;elsevector_.push_back(value);size_++;}// access through [] and getT& operator[](size_t i){return get(i);}T& get(size_t i){if (i < array_.size())return array_[i];elsereturn vector_[i - array_.size()];}    size_t size() {return size_;}bool empty() {return size_ == 0;}

2.3 添加copy构造数据

添加了copy构造处理,从而支持使用一个相同类型Storage构造另一个Storage,也支持operator=的操作。arry与vector都有响应的copy赋值操作,直接使用即可。
添加copy构造时的数据转移,添加operator赋值时的数据转移,这样就可以把一个变量数据转移到另一个变量上,减少数据的copy过程。
这块也要借用vector和array的右值引用转移处理。

    // copy constructorDataStorage(const DataStorage& storage){vector_ = storage.vector_;array_ = storage.array_;size_ = storage.size_;}DataStorage& operator=(const DataStorage& storage){vector_ = storage.vector_;array_ = storage.array_;size_ = storage.size_;}// right referenceDataStorage(DataStorage&& storage){vector_ = std::move(storage.vector_);array_ = std::move(storage.array_);size_ = storage.size_;storage.size_ = 0;}DataStorage& operator=(DataStorage&& storage){vector_ = std::move(storage.vector_);array_ = std::move(storage.array_);size_ = storage.size_;storage.size_ = 0;}

2.4 添加Iterator访问支持

添加iterator访问接口支持

	class Iterator{public:Iterator(DataStorage* p, const size_t idx) : data_(p), idx_(idx){}private:friend class DataStorage; size_t idx_;DataStorage* data_;};};// IteratorIterator begin(){return Iterator(this, 0);}Iterator end(){return Iterator(this, size_);}

2.5 为Iterator增加自增操作与相等判定操作

为了支持这样方式的使用 for(auto iter=xxx.begin(); iter != xxx.end(); iter++);
需要增加支持自增操作,++iter,与iter++支持,判断iter是否相等的支持 != ==。
需要增加支持*iter取值,也支持T为复杂结构时,取T中的元素->方式取值。
在Iterator类中添加定义:

	  // pre increment ++iter, Iterator& operator++(){idx_++;return *this;}// post increment iter++Iterator operator++(int){Iterator before = *this;idx_++;return before;}// operator = and !=bool operator==(const Iterator& it){return idx_ == it.idx_ && data_ == it.data_;}bool operator!=(const Iterator& it){return idx_ != it.idx_ || data_ != it.data_;}T& operator*(){return data_->get(idx_);}T* operator ->(){return &data_->get(idx_);}

2.6 增加reverse_iterator逆序遍历支持

通过iterator可以方便实现正序遍历,逆序遍历也一样,也可以同样进行支持

    class reverse_iterator : public Iterator{public:reverse_iterator(DataStorage* p, const size_t idx) : Iterator(p, idx){}// pre increment ++i, --ireverse_iterator& operator++(){this->idx_--;return *this;}// post increment i++, i--reverse_iterator operator++(int){auto before = *this;this->idx_--;return before;}};// reverse iteratorreverse_iterator rbegin() {return reverse_iterator(this, size() - 1);}reverse_iterator rend() {return reverse_iterator(this, -1);}

3. 验证DataStorage类型

验证DataStorage是否可用,写简单的赋值、取值、遍历程序,来验证DataStroage的可用性。

  • 验证放置数据push_back;
  • 验证[]方式访问数据,设置数据
  • 验证[]方式打印数据列表
  • 验证&&右值引用方式做数据转移
  • 验证iter方式访问数据,遍历数据,iter取值
  • 验证=赋值方式copy数据
  • 验证for each的iter方式取数据
  • 逆序iter遍历支持

验证程序如下:

int main(int argc, char** argv){DataStorage<int, 3> data;for (int i=0; i<10; i++)data.push_back(i);  data[9] = 9999;for (int i=0; i<data.size(); i++)printf("data[%d] = %d\n", i, data[i]);DataStorage<int, 3> other = std::move(data); for (auto iter=data.begin(); iter != data.end(); iter++)printf("*iter = %d\n", *iter);data = other;for (auto val : data)printf("each = %d\n", val); for (auto iter=data.rbegin(); iter != data.rend(); iter++)printf("*reverse_iter = %d\n", *iter);return 0;
}

4. 扩展

进一步扩展DataStorage模板类,可以考虑添加反向遍历的迭代器const_iterator、和const_reverse_iterator类;
也可以考虑添加erase方法,支持删除数据;
也可以考虑添加insert方法,从某个iter位置向前添加数据;
还可以支持一些常用接口swap,clear,shrink_to_fit相关操作。
在erase设计时,要考虑数据删除后,后续数据的前移,std::array中的部分数据前移,std::vector中的数据前移。
在insert设计时,要考虑数据插入时,数据的后移,数据后移后流出的位置放置新的数据。
erase与insert代码实现形如:

  • 注意使用std::memmove做数据赋值,用于支持copy的src区域和dest区域有重合的情况。
  • 另外std::memove的第三个字段是字节数量,注意个数乘以sizeof(T)来计算copy的字节数。
  • 区分插入或删除位置位于array空间内还是vector空间内,vector空间内,可以直接交给vector的内部函数做处理;位于array空间内的,需要自定义处理,同时处理array空间部分和vector空间部分。
    Iterator erase(Iterator& iter){if (iter.idx() < array_.size()){std::memmove(array_.data() + iter.idx(), array_.data() + iter.idx() + 1, (array_.size() - iter.idx() - 1)*sizeof(T));if (size() > array_.size()){array_.back() = vector_.front();vector_.erase(vector_.begin());}}else{auto itererase = vector_.begin() + (iter.idx() - array_.size());vector_.erase(itererase);}size_--;return iter;}Iterator insert(Iterator& iter, const T& value){if (iter.idx() < array_.size()){if (size() >= array_.size()){vector_.insert(vector_.begin(), array_.back());}std::memmove(array_.data() + iter.idx() + 1, array_.data() + iter.idx(), (array_.size() - iter.idx() - 1)*sizeof(T));*iter = value;}else{auto iteradd = vector_.begin() + (iter.idx() - array_.size());vector_.insert(iteradd, value);}size_++;return iter;}

验证代码:

  for (auto iter=data.begin(); iter != data.end(); iter++)iter = data.erase(iter);printf("after erase\n"); for (auto iter=data.begin(); iter != data.end(); iter++)printf("*iter = %d\n", *iter);for (auto iter=data.begin(); iter != data.end(); iter++){iter = data.insert(iter, *iter - 1);iter++;}printf("after insert\n");for (auto iter=data.begin(); iter != data.end(); iter++)printf("*iter = %d\n", *iter); 

5. 附录

附录vector支持的常用接口列表:

Member functions

func nameinfo
(constructor)constructs the vector
(destructor)destructs the vector
operator=assigns values to the container
assignassigns values to the container
get_allocatorreturns the associated allocator

Element access

func nameinfo
ataccess specified element with bounds checking
operator[]access specified element
frontaccess the first element
backaccess the last element
data(C++11) direct access to the underlying array

Iterators

func nameinfo
begin/cbeginreturns an iterator to the beginning
end/cendreturns an iterator to the end
rbegin/crbeginreturns a reverse iterator to the beginning
rend/crendreturns a reverse iterator to the end

Capacity

func nameinfo
emptychecks whether the container is empty
sizereturns the number of elements
max_sizereturns the maximum possible number of elements
reservereserves storage
capacityreturns the number of elements that can be held in currently allocated storage
shrink_to_fit(C++11) reduces memory usage by freeing unused memory

Modifiers

func nameinfo
clearclears the contents
insertinserts elements
emplace(C++11) constructs element in-place
eraseerases elements
push_backadds elements to the end
emplace_back(C++11) constructs elements in-place at the end
pop_backremoves the last element
resizechanges the number of elements stored
swapswaps the contents

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/52009.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Hadoop】核心组件深度剖析:HDFS、YARN与MapReduce的奥秘

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《大数据前沿&#xff1a;技术与应用并进》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、Hadoop简介 2、Hadoop生态系统概览 二、Hadoo…

[Sqlserver][索引]SQL Server 索引概述

SQL Server 索引概述 索引简介 目的&#xff1a;提升SQL Server性能&#xff0c;加快查询速度&#xff0c;减少响应时间。限制&#xff1a;合理使用索引&#xff0c;避免过多索引影响数据更新操作和浪费硬盘空间。 索引分类 唯一索引 (UNIQUE)&#xff1a;确保索引值唯一。…

如何做萤石开放平台的物联网卡定向?

除了用萤石自带的4G卡外&#xff0c;我们也可以自己去电信、移动和联通办物联网卡连接萤石云平台。 1、说在前面 注意&#xff1a;以下流程必须全部走完&#xff0c;卡放在设备上才能连接到萤石云平台。 2、大致流程 登录官网→下载协议→盖章&#xff08;包括骑缝章&#…

源码阅读入门:以ArrayList为例

阅读源码是提升编程技能的重要方法。以Java集合框架中的ArrayList为例,引导如何有效地阅读和理解源码。 第一步&#xff1a;选择合适的源码 选择合适的源码是成功的第一步。对于初学者来说&#xff0c;可以从简单的类开始&#xff0c;比如String、ArrayList或者HashMap。 第二…

Content-Type 和 请求体

Content-Type 和请求体-经验笔记 概述 在 Web 开发中&#xff0c;Content-Type 是 HTTP 头部的一个重要组成部分&#xff0c;它指定了客户端发送给服务器的数据格式。理解 Content-Type 的含义及如何使用它是构建高效且兼容性良好的 API 的基础。 Content-Type 基础 定义&a…

开闭原则(Open-Closed Principle, OCP)详解

开闭原则&#xff08;Open-Closed Principle, OCP&#xff09;详解 在软件设计领域&#xff0c;设计模式是解决问题的一套经过验证的、可复用的设计方案。设计模式中的六大原则为软件开发提供了重要的指导&#xff0c;其中开闭原则&#xff08;Open-Closed Principle, OCP&…

多平台编译libexif

下载地址&#xff1a;https://github.com/libexif/libexif/releases 1. ubuntu x64 &#xff08;银河麒麟系统aarch64步骤相同&#xff09; # 解压 > tar -jxvf libexif-0.6.24.tar.bz2 > cd libexif-0.6.24 # 配置 > ./configure # 编译 > make # 安装 > mak…

leetcode + react学习

上午 后端又没进到我的需求&#xff0c;我请问呢&#xff1f; 然后继续栈和队列 的代码随想录 js里面没有特别的数据结构&#xff0c;一般就是用数组来模拟栈和队列。栈和队列是线性&#xff0c;堆是二叉树&#xff0c;通常用来实现优先队列。 栈适用于匹配问题。 下午 栈…

MySQL:从入门到放弃

基础查询 MySQL&#xff1a;基础查询 Mybatis&#xff1a;基础巩固-DDL 项目实战 MySQL&#xff1a;按照日期分组查询 查询开始时间与结束时间在指定的日期范围之内&#xff0c;并且结束时间可以为NULL的数据

【C++】String类:标准库介绍

目录 一.预备知识 1.auto关键字 2.范围for 3.迭代器 二.标准库里的string 1.string类的基本介绍 2.构造函数 ​编辑 3.访问及遍历操作 3.1 operator [] 3.2 基于范围for 3.3 使用迭代器 4.迭代器 5.容量操作 5.1 size和length 5.2 capacity 5.3 reserve和resiz…

wordpress二次开发 在Woocommerce相关产品中显示产品变体的方法

在Woocommerce中&#xff0c;相关产品的展示是一个很好的促销策略。但有时候&#xff0c;你可能希望在这些相关产品中显示产品的不同变体&#xff0c;以提供更多选择给客户。本文将指导你如何在相关产品中显示产品变体。 首先&#xff0c;你需要登录到你的WordPress管理后台。…

备考2024年美国数学竞赛AMC10:吃透1250道真题和知识点(持续)

有什么含金量比较高的初中生数学竞赛吗&#xff1f;美国数学竞赛AMC10是个不错的选择。那么&#xff0c;如何备考AMC10美国数学竞赛呢&#xff1f;做真题&#xff0c;吃透真题和背后的知识点是备考AMC8、AMC10有效的方法之一。 通过做真题&#xff0c;可以帮助孩子找到真实竞赛…

Python使用Selenium进行Web自动化测试详解

目录 引言 一、Selenium简介 Selenium的核心组件 二、环境搭建 1. 安装Python 2. 安装Selenium库 3. 下载并配置浏览器驱动 三、基础用法 1. 启动浏览器 2. 定位页面元素 3. 元素操作 4. 等待元素加载 1. 测试目的 2. 测试步骤与代码实现 3. 注意事项 结论 引言…

【Python深度学习】图片识别任务中,原始数据集中图片的大小不固定时,用代码设置大小为多少合适?

文章目录 图片大小设置多少合适如何用代码实现方法一:使用 Pillow(PIL)方法二:使用 OpenCV注意事项在做图片分类识别任务时,如果没有公开数据集的情况下,需要自己去网上找相应的图片数据,但是各种各样的图片大小不一致,手动截图的话,大小无法保证,所以此时,最好就是…

ViT笔记学习

1.VIT ViT原理讲解 ViT结合代码 1.3 ViT模型架构 我们先结合下面的动图来粗略地分析一下ViT的工作流程&#xff0c;如下&#xff1a; 将一张图片分成patches将patches铺平将铺平后的patches的线性映射到更低维的空间添加位置embedding编码信息将图像序列数据送入标准Transfor…

labview经验分享1-任意16进制字符类型匹配

系列文章目录 1、任意16进制字符类型匹配 文章目录 系列文章目录问题导入实现任意16进制字符类型匹配在这里插入图片描述 总结 问题导入 labveiw的字符串匹配&#xff0c;使用的是正则表达式&#xff0c;可以让我们很方便的对字符串进行字符处理操作。 但是某些情况下&#…

【python】Python实现XGBoost算法的详细理论讲解与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

encoding with ‘idna‘ codec failed (UnicodeError: label empty or too long)

今天在使用Flask连接mysql的时候&#xff0c;遇到了一个报错&#xff1a;encoding with ‘idna’ codec failed (UnicodeError: label empty or too long) 网上查了一下说是字符集的问题&#xff0c;然后尝试修改了一下字符集&#xff0c;结果还是不行。 最后去翻阅SQLAlchemy…

使用docker-compose运行kafka及验证(无需zookpeer)

前言&#xff1a;要求安装docker-compose kafka镜像版本&#xff1a;apache/kafka:3.8.0 可能存在镜像拉不下来的情况&#xff1a; 1、vim /etc/docker/daemon.json {"data-root":"/data/docker","registry-mirrors": ["https://docker.m…

redis 主从复制方案

redis 一、安装二、创建服务三、开启 redis 持久化四、开启主从配置修改 master 的主配置文件修改 slave1 和 slave2 的主配置文件 五、测试 环境准备 准备三台系统为CentOS7的主机 master&#xff1a;192.168.152.71slave1&#xff1a;192.168.152.72slave2&#xff1a;192.1…