(C++ STL)vector类的简单模拟实现与源码展示

news2024/12/23 9:21:29

vector类的简单模拟实现

  • 一、前言
  • 二、vector 的成员变量
  • 三、vector 部分函数实现
    • size、capacity
    • reserve
    • resize
    • insert 与注意事项
    • erase
    • 构造、析构、赋值拷贝
  • 四、vector 源代码

以下代码环境为 VS2022 C++。

一、前言

vector类 本质上就是数据结构中的顺序表。(可参考:顺序表的讲解与实现)

接下来我们来简单实现 vector类 和 部分对应函数。

参考:legacy.cplusplus.com中的 std::vector

二、vector 的成员变量

在 vector.hpp 中:

namespace my
{
	template<class T>
	class vector
	{
        // Vector的迭代器是一个原生指针
        typedef T* iterator;
        typedef const T* const_iterator;

        iterator begin()
        {
            return _start;
        }

        iterator end()
        {
            return _finish;
        }

        const_iterator begin() const
        {
            return _start;
        }

        const_iterator end() const
        {
            return _finish;
        }

	private:

        iterator _start = nullptr;        // 指向数据块的开始
        iterator _finish = nullptr;       // 指向有效数据的尾
        iterator _endOfStorage = nullptr; // 指向存储容量的尾

	};
}

vector 的成员变量:

  1. vector 所存储数据的类型不确定,则使用模版。
  2. vector 的迭代器本质是指针变量,指向内存的地址。
  3. vector 有三个成员变量,_start、_finish、_endOfStorage,类型为 iterator。本质上都是指针变量,用来定位 vector 在内存上的位置。

vector 在内存中分布如图:

在这里插入图片描述

  1. 红色方块表示未开辟的空间,绿色方块表示有效数据,蓝色方块表示剩余存储容量。

  2. 这里采用 左闭右开 [ _start,_finish ),即 _start 开始就指向有效元素,_finish 指向最后一个元素的后面,_endOfStorage 同理。

三、vector 部分函数实现

size、capacity

vector 的有效长度和容量,是通过指针计算得到的。

指针 - 指针 得到的是中间的元素个数。

在 my::vector 中:

        size_t size() const
        {
            return _finish - _start;
        }

        size_t capacity() const
        {
            return _endOfStorage - _start;
        }

reserve

参考:std::vector::reserve

C++ 规定:

  1. 原空间 小于 需求空间 时,vector 扩容到需求空间或更多。

  2. 原空间 大于或等于 需求空间 时,vector 容量不变。

这里扩容严格按照 2 倍扩容。

在 vector.hpp 中:

template<class T>
void my::vector<T>::reserve(size_t n)
{
    if (n <= capacity())
    {
        return;
    }

    size_t newCapacity = capacity() == 0 ? MY_VECTOR_INIT_CAPACITY : capacity() * 2;

    while (newCapacity < n)
    {
        newCapacity <<= 1;
    }

    iterator newStart = new T[newCapacity];

    // 只能拷贝内置类型,如 string等非内置类型会失效
    //memcpy(newStart, _start, sizeof(T) * size());

    // 使用赋值拷贝
    for (int i = 0; i < size(); ++i)
    {
        newStart[i] = _start[i];
    }

    _finish = newStart + size();            // 指向代码不能交换位置
    _endOfStorage = newStart + newCapacity;

    delete[] _start;
    _start = newStart;
}

resize

参考:std::vector::resize

在 vector.hpp 中:

template<class T>
void my::vector<T>::resize(size_t n, const T& value)
{
    if (n > capacity())
    {
        reserve(n);

        iterator it = end();
        iterator over = begin() + n;

        while (it != over)
        {
            *it = value;
        }
    }

    _finish = begin() + n;
}

insert 与注意事项

参考:std::vector::insert

注意:
如果返回类型是在类里定义的,在类外使用时需要加上 typename 确定是类型,否则报错。

如 my::vector<T>::iterator 是类里 typedef 的一个类型,在类外使用时,编译器不能确定它是模版类的类型还是其静态变量,则会报错。

在 vector.hpp 中:

// 编译器不能分辨 my::vector<T>::iterator 是类型还是静态变量,
// 加上 typename 表示其为类型即可解决 
template<class T>   
typename my::vector<T>::iterator my::vector<T>::insert(my::vector<T>::iterator pos, const T& x)
{
    assert(_start <= pos && pos <= _finish);

    size_t sz = pos - begin();  // 考虑到扩容后 pos 可能失效,用中间值储存

    reserve(size() + 1);

    iterator it = nullptr;
    for (it = end(); it != begin() + sz; --it)
    {
        *it = *(it - 1);
    }
    *it = x;

    resize(size() + 1);

    return it + 1;
}

erase

参考:std::vector::erase

在 vector.hpp 中:

template<class T>
typename my::vector<T>::iterator my::vector<T>::erase(my::vector<T>::iterator pos)
{
    assert(size() != 0);
    assert(_start <= pos && pos < _finish);

    for (iterator it = pos; it != end() - 1; ++it)
    {
        *it = *(it + 1);
    }
    --_finish;

    return pos;
}

构造、析构、赋值拷贝

参考:std::vector::vector
参考:std::vector::~vector
参考:std::vector::operator=

在 my::vector 中:

        vector() = default; // default 后编译器会自动生成默认构造

        vector(size_t n, const T& value = T())
        {
            reserve(n);
            resize(n);

            for (auto& val : *this)
            {
                val = value;
            }
        }

        vector(int n, const T& value = T())
        {
            reserve(n);
            resize(n);

            for (auto& val : *this)
            {
                val = value;
            }
        }

        template<class InputIterator>
        vector(InputIterator first, InputIterator last)
        {
            while (first != last)
            {
                push_back(*first);

                ++first;
            }
        }

        vector(const vector<T>& v)
        {
            reserve(v.capacity());
            for (auto& val : v)
            {
                push_back(val);
            }
        }

        void swap(vector<T>& v)
        {
            std::swap(_start, v._start);
            std::swap(_finish, v._finish);
            std::swap(_endOfStorage, v._endOfStorage);
        }

        vector<T>& operator= (vector<T> v)
        {
            swap(v);

            return *this;
        }

        ~vector()
        {
            if (_start != nullptr)
            {
                delete[] _start;
                _start = nullptr;
            }

            _finish = _endOfStorage = _start;
        }

四、vector 源代码

在 vector.hpp 中:

#pragma once

#include <iostream>
#include <cassert>

#define MY_VECTOR_INIT_CAPACITY 4

namespace my
{
    template<class T>
    class vector
    {
    public:

        // Vector的迭代器是一个原生指针
        typedef T* iterator;

        typedef const T* const_iterator;

        iterator begin()
        {
            return _start;
        }

        iterator end()
        {
            return _finish;
        }

        const_iterator begin() const
        {
            return _start;
        }

        const_iterator end() const
        {
            return _finish;
        }

        //--------------------------------------

        vector() = default; // default 后编译器会自动生成默认构造

        vector(size_t n, const T& value = T())
        {
            reserve(n);
            resize(n);

            for (auto& val : *this)
            {
                val = value;
            }
        }

        vector(int n, const T& value = T())
        {
            reserve(n);
            resize(n);

            for (auto& val : *this)
            {
                val = value;
            }
        }

        template<class InputIterator>
        vector(InputIterator first, InputIterator last)
        {
            while (first != last)
            {
                push_back(*first);

                ++first;
            }
        }

        vector(const vector<T>& v)
        {
            reserve(v.capacity());
            for (auto& val : v)
            {
                push_back(val);
            }
        }

        void swap(vector<T>& v)
        {
            std::swap(_start, v._start);
            std::swap(_finish, v._finish);
            std::swap(_endOfStorage, v._endOfStorage);
        }

        vector<T>& operator= (vector<T> v)
        {
            swap(v);

            return *this;
        }

        ~vector()
        {
            if (_start != nullptr)
            {
                delete[] _start;
                _start = nullptr;
            }

            _finish = _endOfStorage = _start;
        }

        //--------------------------------------

        size_t size() const
        {
            return _finish - _start;
        }

        size_t capacity() const
        {
            return _endOfStorage - _start;
        }

        void reserve(size_t n);

        void resize(size_t n, const T& value = T());

        //--------------------------------------

        T& operator[](size_t pos)
        {
            return *(begin() + pos);
        }

        const T& operator[](size_t pos) const
        {
            return *(begin() + pos);
        }

        //--------------------------------------

        void push_back(const T& x)
        {
            insert(end(), x);
        }

        void pop_back()
        {
            erase(end() - 1);
        }

        iterator insert(iterator pos, const T& x);

        iterator erase(iterator pos);

    private:

        iterator _start = nullptr;        // 指向数据块的开始
        iterator _finish = nullptr;       // 指向有效数据的尾
        iterator _endOfStorage = nullptr; // 指向存储容量的尾
    };

}

template<class T>
void my::vector<T>::reserve(size_t n)
{
    if (n <= capacity())
    {
        return;
    }

    size_t newCapacity = capacity() == 0 ? MY_VECTOR_INIT_CAPACITY : capacity() * 2;

    while (newCapacity < n)
    {
        newCapacity <<= 1;
    }

    iterator newStart = new T[newCapacity];

    // 只能拷贝内置类型,如 string等非内置类型会失效
    //memcpy(newStart, _start, sizeof(T) * size());

    // 使用赋值拷贝
    for (int i = 0; i < size(); ++i)
    {
        newStart[i] = _start[i];
    }

    _finish = newStart + size();            // 指向代码不能交换位置
    _endOfStorage = newStart + newCapacity;

    delete[] _start;
    _start = newStart;
}

template<class T>
void my::vector<T>::resize(size_t n, const T& value)
{
    if (n > capacity())
    {
        reserve(n);

        iterator it = end();
        iterator over = begin() + n;

        while (it != over)
        {
            *it = value;
        }
    }

    _finish = begin() + n;
}

// 编译器不能分辨 my::vector<T>::iterator 是类型还是静态变量,
// 加上 typename 表示其为类型即可解决 
template<class T>   
typename my::vector<T>::iterator my::vector<T>::insert(my::vector<T>::iterator pos, const T& x)
{
    assert(_start <= pos && pos <= _finish);

    size_t sz = pos - begin();  // 考虑到扩容后 pos 可能失效,用中间值储存

    reserve(size() + 1);

    iterator it = nullptr;
    for (it = end(); it != begin() + sz; --it)
    {
        *it = *(it - 1);
    }
    *it = x;

    resize(size() + 1);

    return it + 1;
}

template<class T>
typename my::vector<T>::iterator my::vector<T>::erase(my::vector<T>::iterator pos)
{
    assert(size() != 0);
    assert(_start <= pos && pos < _finish);

    for (iterator it = pos; it != end() - 1; ++it)
    {
        *it = *(it + 1);
    }
    --_finish;

    return pos;
}

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

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

相关文章

【最新华为OD机试E卷】boos的收入(100分)-多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,…

4.负载均衡

文章目录 1.多级部署2.实现请求计数器3.负载均衡3.1服务端负载均衡3.2客户端负载均衡3.3自定义负载均衡3.4负载均衡策略3.5 LoadBalance 原理 4.部署实现 大家好&#xff0c;我是晓星航。今天为大家带来的是 负载均衡 相关的讲解&#xff01;&#x1f600; 1.多级部署 复制一…

C语言 | Leetcode C语言题解之第378题有序矩阵中第K小的元素

题目&#xff1a; 题解&#xff1a; bool check(int **matrix, int mid, int k, int n) {int i n - 1;int j 0;int num 0;while (i > 0 && j < n) {if (matrix[i][j] < mid) {num i 1;j;} else {i--;}}return num > k; }int kthSmallest(int **matri…

面试题小总结

一、为什么要使用Redis&#xff1f; 因为它是内存数据库&#xff0c;运行速度快因为它的工作线程是单线程&#xff0c;具有串行化&#xff0c;原子性具有IO模型&#xff0c;天生支撑高并发是kv模型&#xff0c;v具有多个数据结构具有本地方法&#xff0c;可以计算数据移动是二…

Mac用户必备:轻松添加Git SSH密钥全攻略

最近新买了一台MacBook笔记本&#xff0c;然后安装了git&#xff0c;准备下载代码&#xff0c;正好遇到配置GitHub的ssh密钥&#xff0c;记录一下整个操作流程。 操作步骤 在Mac上添加Git SSH密钥的步骤如下&#xff1a; 检查是否已有SSH密钥&#xff1a; 打开终端&#xff0…

Nginx: https解决安全问题

https原理 1 &#xff09;http协议存在的问题 数据使用明文传输&#xff0c;可能被黑客窃取 (需要信息加密)报文的完整性无法验证&#xff0c;可能被黑客篡改 (需要完整性校验)无法验证通信双方的身份&#xff0c;可能被黑客伪装 (需要身份认证) 2 ) https 原理 所谓 https,…

新160个crackme - 043-riijj_cm_20041121

运行分析 除了主程序还有一个dll文件&#xff0c;应该是要加载pf1.dll这个动态链接库运行主程序&#xff0c;需破解Name和Serial&#xff0c;点击注册无反应 PE分析 C程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 尝试ida动调加载动态链接库pf1.dll&#xff0c…

全能型AI“草莓”:未来趋势还是市场泡沫?

你好&#xff0c;我是三桥君 近日&#xff0c;OpenAI宣布将在秋季推出代号为“草莓”的新AI模型。这一消息迅速引起了科技界和市场的广泛关注。 OpenAI的新项目“草莓”&#xff08;Strawberry&#xff09;是一个备受关注的人工智能模型&#xff0c;预计将在今年秋季发布。这个…

算法复盘——LeetCode hot100:哈希

文章目录 哈希表哈希表的基本概念哈希表的使用1. 插入操作2. 查找操作3. 删除操作 哈希表的优点和缺点1.两数之和复盘 242.有效的字母异位词复盘 49.字母异位词分组复盘 128. 最长连续序列复盘HashSet 哈希表 先来搞清楚什么是哈希表吧~ 概念不清楚方法不清楚怎么做题捏 哈希表…

MongonDB-索引

一、索引-index (一) 概述 索引支持在MongoDB中高效地执行查询。如果没有索引&#xff0c;MongoDB必须执行全集合扫描&#xff0c;即扫描集合中的每个文档&#xff0c;以选择与查询语句匹配的文档。这种扫描全集合的查询效率是非常低的&#xff0c;特别在处理大量的数据时&am…

firewalld 防火墙常用命令,新手必看

firewalld 防火墙常用命令 防火墙状态命令 systemctl start firewalld #启动防火墙 systemctl stop firewalld #关闭防火墙 systemctl restart firewalld #重启防火墙 systemctl enable firewalld #设置开机自启 systemctl disable firewalld #禁用开机自启 systemctl s…

自己开发完整项目一、登录功能-03(使用springSecurity安全框架,查询用户角色权限)

一、说明 在前面两章节&#xff0c;实现了通过springsecurity来进行用的登录认证&#xff0c;当用户输入用户名和密码之后&#xff0c;通过额数据库中的信息比对&#xff0c;比对成功那么放行。但是还存在一个问题&#xff1a;因为系统的所有页面包括按钮都是有各自的权限&…

全网最全robotframework自动化测试环境搭建

一、前言 1、在2019年之前&#xff0c;robotframework-ride的版本一直是1.5.2.1&#xff0c;是2016年1月份的版本&#xff0c;只能安装在python2.7的环境上&#xff0c;导致如果想同时使用robotframework做测试且又需要python3环境编写python代码的小伙伴都需要在操作系统上安…

Golang 读取文件

GoLang读取文件需要用到os类去打开文件&#xff0c;然后再用其他方式分析文件里的内容。打开文件比较简单&#xff0c;使用os.Open就可以了&#xff0c;记住用defer关闭就行。但是读取文件内容就头疼了&#xff0c;以文本文件为例子&#xff0c;就有各种方式 读取到byte数组 首…

渐进式衰老?医美三剑客的“市梦率”幻灭了

医美股神话彻底幻灭了。 从股价蒸发到业绩失速&#xff0c;“医美三剑客”上演着繁华落幕&#xff0c;回归平凡的剧本。 近一年来&#xff0c;爱美客、华熙生物、昊海生科股价分别累计下跌52.97%、46.03%、16.09%。 “医美三剑客”近一年累计跌幅&#xff08;资料来源&#x…

分水岭算法简介

分水岭算法是一种经典的图像分割技术&#xff0c;广泛应用于图像处理领域。它的名称源自地理学中的“分水岭”概念&#xff0c;即在地形中&#xff0c;水从高处流向低处&#xff0c;最终汇聚成河流的过程。在图像分割中&#xff0c;分水岭算法将灰度图像视为地形&#xff0c;将…

STM32(八):定时器——输入捕获实验

目录 输入捕获模式测频率&#xff1a; 结构图&#xff1a; 步骤&#xff1a; 部分函数详解&#xff1a; 源码&#xff1a; PWMI模式测频率占空比&#xff1a; 结构图&#xff1a; ​编辑 举例说明 源码&#xff1a; 输入捕获模式测频率&#xff1a; 结构图&#xf…

using showdown js with openAi streaming response

题意&#xff1a;"使用 Showdown.js 处理 OpenAI 流式响应" 问题背景&#xff1a; I tried using showdownjs to translate streamed markdown from OpenAi to HTML "我尝试使用 Showdown.js 将来自 OpenAI 的流式 Markdown 转换为 HTML" I changed the …

机器学习第五十四周周报 MAGNA

文章目录 week54 MAGNA摘要Abstract一、文献阅读1. 题目2. Abstract3. 文献解读3.1 Introduce3.2 创新点 4. MAGNA4.1 基础4.2 多跳注意力扩散机制4.3 网络架构4.4 图注意力扩散机制的分析 5. 实验分析5.1 节点分类5.2 知识图谱 6.结论 二、若依系统1. 权限管理2. 数据字典3. 其…

【Linux修行路】进程通信——共享内存

目录 ⛳️推荐 一、直接原理 1.1 共享内存的的申请 1.2 共享内存的释放 二、代码演示 2.1 shmget 2.1.1 详谈key——ftok 2.2 创建共享内存样例代码 2.3 获取共享内存——进一步封装 2.4 共享内存挂接——shmat 2.5 共享内存去关联——shmdt 2.6 释放共享内存——s…