当前位置: 首页 > news >正文

C++第二十一弹---vector深度剖析及模拟实现(上)

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】

目录

1、基本结构

2、默认成员函数

2.1、构造函数

2.2、析构函数 

2.3、拷贝构造函数 

2.3、赋值操作符重载

3、数据访问

4、迭代器获取

总结


1、基本结构

首先定义一个vector模版类,其中三个成员变量均为迭代器,且此处vector的迭代器是一个原生指针,我们这里为其定义别名iterator。

namespace lin 
{template<class T>//此处为类模板,实现不同数据类型的存储class vector{public://vector迭代器为原生指针typedef T* iterator;//定义指针类型的别名iteratortypedef const T* const_iterator;//...函数接口的实现private:iterator _start;// 指向容器的开始iterator _finish;// 指向容器中最后一个有效数据的下一个位置iterator _endofstorage;  // 指向存储容量的结尾};
}

私有成员变量:

_start: 这是一个指针,指向容器的第一个元素。
_finish: 这个指针指向容器中最后一个有效数据的下一个位置。
_endofstorage: 这个指针指向分配给vector的内存块的末尾。这不是最后一个有效元素的位置,而是整个内存块的结束位置,在这之后可能会有额外的未初始化空间,预留以实现当vector增长时无需重新分配整个数组。

2、默认成员函数

2.1、构造函数

vector()

默认构造的函数功能是构造一个没有元素的空容器。

默认构造函数实现有两种方式,第一种是直接通过初始化列表直接初始值,第二种是通过成员变量的缺省值

1.初始化列表

//将成员变量都初始化为空
vector(): _start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{}

2. 缺省值

//会默认使用缺省值构造
vector()
{}private:iterator _start=nullptr;		iterator _finish=nullptr;		iterator _endofstorage=nullptr;  
};

 vector(size_t n, const T& val = T()) 

填充构造函数功能是构造一个包含 n 个元素的容器。每个元素都是 val 值。

思想:

为了确保空间足够,我们可以先将空间扩容至n(减少扩容消耗)再将n个值尾插到容器中

//构造n个数值 并初始化为val
vector(size_t n, const T& val = T())
{reserve(n);//细节,先开空间for (size_t i = 0; i < n; i++){push_back(val);}
}

注意:

vector构造函数中的const T& val = T()T是一种类型,T的类型取决于vector后的<>,<>中是什么类型,T就是什么类型。

T是内置类型时,T() == 0。

char()需要强转成int才能看到0,因为ASCII码值为0的char字符是'\0',屏幕上显示不出来。

当T是自定义类型时,T()调用无参或者全缺省构造函数构造一个匿名对象,若T类中不存在无参全缺省构造方法时报错。

  vector(InputIterator first, InputIterator last)

迭代器区间构造函数功能是将[first,last)区间的元素构造成一个新容器,注意 last位置的元素是不取的。

  1. template <class InputIterator> 是一个函数模板函数参数是一个迭代器类型
  2. 函数实现原理:从 first 位置开始遍历迭代器区间,遍历的同时将该位置的数据尾插到vector上,直到遍历到 last 位置则结束循环
//类模板里面有函数模板
//template<typename InputIterator>
template <class InputIterator>//与类模板类型名区分
vector(InputIterator first, InputIterator last)//拷贝迭代器区间
{while (first != last)//起始与结尾不相等则插入{push_back(*first);++first;//向后移动}
}

 如果实现了迭代器区间构造函数以及填充构造函数之后,去实例化vector<int> v(10,1)则会出错,因为此时的参数都为int类型,而填充构造函数 vector(size_t n, const T& val = T()) 的第一个类型时无符号整数,迭代器区间构造函数  vector(InputIterator first, InputIterator last)是两个相同类型的模板参数,此时会优先调用更匹配的迭代器区间构造函数,然后10-1不是一个区间,也不能解引用,因此编译器报错。

解决办法是重载一个填充构造函数,如下:

  vector(int n, const T& val = T())

//构造n个数值 并初始化为val
vector(int n, const T& val = T())//x64有问题
{reserve(n);//细节,先开空间for (int i = 0; i < n; i++){push_back(val);}
}

2.2、析构函数 

析构函数功能是销毁动态开辟的空间。

释放空间并将指针置空即可。

~vector()
{delete[] _start;_start = _finish = _endofstorage = nullptr;
}

2.3、拷贝构造函数 

vector(const vector<T>& v)

函数功能构造一个包含 v 中所有元素的容器。

先开辟一个与原容器大小相等的空间,然后直接尾插即可。

//v(v1)
vector(const vector<T>& v)
{reserve(v.capacity());//开始扩容,减少频繁扩容for (auto& x : v)//字符串可能有问题,加&{push_back(x);//尾插数据}
}

 注意:reserve()与push_back()函数在后面有实现!!!

vector(initializer_list<T> il) 

initializer_list 是一个类,支持{}初始化。 

实现思想是使用范围for遍历该类,遍历的同时尾插数据即可,为了减少扩容消耗,可以提前开辟好空间。 

//vector<int> v ={1,2,3,4,5}
vector(initializer_list<T> il)
{reserve(il.size());//提前开好il大小的空间for (auto& x : il){push_back(x);//尾插}
}

2.3、赋值操作符重载

 vector<T>& operator=(vector<T> v)

赋值操作符重载的功能是将新内容分配给容器,替换其当前内容,并相应地修改其大小。

此处为现代写法,直接交换两个容器,函数参数不能加引用。

void swap(vector<T>& v)
{swap(_start, v._start);//调用库函数的swap函数swap(_finish, v._finish);swap(_endofstorage, v._endofstorage);
}
vector<T>& operator=(vector<T> v)
{swap(v);//调用就近类内函数return *this;
}

3、数据访问

operator[](size_t pos)

使用下标加 [ ] 访问数据。

T& operator[](size_t pos)//[]运算符重载
{assert(pos < size());return _start[pos];
}
const T& operator[](size_t pos) const
{assert(pos < size());return _start[pos];
}

 front()

获取第一个元素(即_start指向的位置)。

T& front()
{return *_start;
}const T& front()const
{return *_start;
}

 back()

获取最后一个元素(_finish前面一个位置)。

T& back()
{return *(_finish - 1);
}const T& back()const
{return *(_finish - 1);
}

4、迭代器获取

begin()

返回容器的首地址(_start)。

iterator begin()
{return _start;
}
const_iterator begin() const //无允许修改,加const修饰函数
{return _start;
}

end()

返回容器当中有效数据的下一个数据的地址(_finish)。

iterator end()
{return _finish;
}
const_iterator end() const
{return _finish;
}

总结


本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

相关文章:

  • 基于标准库的STM32的外部中断EXTI
  • MyBatis延迟加载缓存分页逆向工程
  • 【计算机毕业设计】345大学生心理健康测评管理系统小程序
  • Flink实现实时异常登陆监控(两秒内多次登陆失败进行异常行为标记)
  • 网络原理-TCP/IP --传输层(UDP)
  • 艾体宝洞察 | Redis Enterprise对比ElastiCache
  • springboot实现文件上传功能,整合云服务
  • 前端经典手写面试题---节流防抖
  • (含笔试题)深度解析数据在内存中的存储
  • JS-09-es6常用知识1
  • NVIDIA Technologies
  • Spring Boot(七十四):集成Guava 库实现布隆过滤器(Bloom Filter)
  • java学习路径
  • MATLAB基础应用精讲-【数模应用】联合分析
  • 【LINUX】LINUX基础(目录结构、基本权限、基本命令)
  • JavaScript 如何正确处理 Unicode 编码问题!
  • exports和module.exports
  • Hexo+码云+git快速搭建免费的静态Blog
  • JavaScript 无符号位移运算符 三个大于号 的使用方法
  • leetcode386. Lexicographical Numbers
  • Linux gpio口使用方法
  • PaddlePaddle-GitHub的正确打开姿势
  • Redis学习笔记 - pipline(流水线、管道)
  • Swift 中的尾递归和蹦床
  • vue-loader 源码解析系列之 selector
  • webgl (原生)基础入门指南【一】
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 编写高质量JavaScript代码之并发
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 初识 beanstalkd
  • 给初学者:JavaScript 中数组操作注意点
  • 湖南卫视:中国白领因网络偷菜成当代最寂寞的人?
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 如何用vue打造一个移动端音乐播放器
  • 使用agvtool更改app version/build
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • 你对linux中grep命令知道多少?
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • ​一些不规范的GTID使用场景
  • #define 用法
  • #if 1...#endif
  • %@ page import=%的用法
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • *p++,*(p++),*++p,(*p)++区别?
  • ..回顾17,展望18
  • .axf 转化 .bin文件 的方法
  • .NET Core 网络数据采集 -- 使用AngleSharp做html解析
  • .NET 解决重复提交问题
  • .NET关于 跳过SSL中遇到的问题
  • .NET开源项目介绍及资源推荐:数据持久层 (微软MVP写作)
  • @RequestBody的使用
  • [ 网络通信基础 ]——网络的传输介质(双绞线,光纤,标准,线序)
  • []C/C++读取串口接收到的数据程序