C++学习笔记(16)——vector的使用与模拟实现

文章目录

  • 比喻与理解
  • 一、定义
  • 二 、在使用层面
  • 三、在实现层面
  • 四、vector的成员变量
  • 五、vector的迭代器
  • 六、vector的容器函数
  • 七、vector的其他函数
    • 1. reserve更新容器空间大小:vector_name.reserve(size_type);
    • 2. resize大小:vector_name.resize(size_type n);
    • 3. vector的尾删函数: vector_name.pop_back();
    • 4. vector的删除函数: vector_name.erase(size_type);
  • 八、vector的默认构造
    • 1. 默认构造函数:vector<T> vector_name();
    • 2. 带有初始大小的构造函数:vector<T> vector_name(size_type n);
    • 3. 带有初始值和大小的构造函数:vector<T> vector_name(size_type n, const T& value);
    • 4. 带有迭代器范围的构造函数:vector<T> vector_name(InputIterator first,InputIterator last);
    • 5. 拷贝构造函数:vector vector_name_1(vector_name_2);
    • 6. 赋值运算符重载:vector_name_1=vector_name_2;
    • 7. 析构函数: ~vector()
    • 8. const取地址运算符重载: vector_name[size_type n]
    • 9. find()寻找元素


比喻与理解


一、定义

vector是一个可以动态增长的数组,即变长数组。它依靠类和对象的特性实现。

vector比string设计的更合理,减少了很多冗余的库函数。

二 、在使用层面

vector的使用与string相似,初始化与数组类似;
无流插入流提取——vector使用范围for实现打印;

vector的接口与stl保持一致通用。

三、在实现层面

  1. vector是一个可以动态增长的数组,即变长数组。它依靠类和对象的特性实现;
  2. 由于vector的设计在string之后吸收了相关的经验,它的设计比string更加合理;
  3. vector实现时无需手动提供下标与扩容;

不同编译器内部各个功能的实现使用了很多复杂的指针关系,当我们学习理解时应当将其简化成原生指针。

四、vector的成员变量

我们最好在成员变量声明处给出缺省值。

  private:
        //各个成员函数的使用成员变量前全部统一初始化,防止不同编译器的其他初始化
        iterator _start=nullptr; // 指向数据块的开始
        iterator _finish = nullptr; // 指向有效数据的尾
        iterator _endofstorage = nullptr; // 指向存储容量的尾

五、vector的迭代器

vector的结构允许我们使用原生指针去管理它,使用原生指针使其高效管理自身的内容。

// Vector的迭代器是一个原生指针
        typedef T* iterator;
        iterator begin()
        {  
            return _start;
        }

        iterator end()
        {
            return _finish;
        }
        typedef const T* const_iterator;    //迭代器指向的内容不能++

        const_iterator begin() const
        {
            return _start;
        }

        const_iterator end() const
        {
            return _finish;
        }
        const const_iterator cbegin() const
        {
            return _start;
        }
        const const_iterator cend() const
        {
            return _finish;
        }     

六、vector的容器函数

我们使用的定位容器位置的函数,返回的是容器元素的内存位置和相对位置差值。
设置const版本,可以让普通对象和const对象都使用同一个函数定位;

     capacity
        //size_t size()
        //{
        //    return _finish - _start;
        //}

        //size_t capacity()
        //{
        //    return _endofstorage - _start;
        //}

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

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

       

七、vector的其他函数

1. reserve更新容器空间大小:vector_name.reserve(size_type);

适应性地扩大空间所有权,未赋予空间使用权;

void reserve(size_t n)
        {
            if(n>capacity())
            {
                T* tmp = new T[n];
                size_t sz = size();
                if (_start)
                {
                    //memcpy(tmp, _start, sizeof(T) * size());    //浅拷贝
                    
                    for (size_t i = 0; i < sz; i++)
                    {
                        tmp[i] = _start[i];
                    }
                    delete[] _start;
                }
                _start = tmp;
                _finish = sz + _start;
                _endofstorage = _start + n;
            }
        }

我们经判断后认为需要扩容时,会通过以新的空间大小n为准new一个新的对象,再将旧空间的内容深拷贝到新的空间内,其内存地址和容器所有权大小更新,空间使用权大小会保持不变;

2. resize大小:vector_name.resize(size_type n);

刚性地扩大或缩小空间使用权、使用权;

        // T int    
        // int类型没有构造函数的说法,但C++对int模板进行了升级,使其可以在与T()交接的情况下被认为有构造函数
        // T string 
        // t vector<int>
        //T()是匿名对象,用来接收各种数据类型
        void resize(size_t n, const T& value = T()) 
        {
            if (n < size())
            {
                 _finish = _start + n ;
            }
            else  
            { 
                reserve(n);
                while (_finish < _start + n)
                {
                    *_finish = value;
                    ++_finish;
                }
            }
        }
        

扩大或缩小对象的空间所有权大小果需要扩大空间则:

  1. 如果新的空间使用权size大小大于原容器空间所有权capacity大小,则需要同步扩大容器空间所有权capacity和空间使用权size,二者扩容到新地size为止(capacity可能会大于size),然后使用给定的value去填充size未利用空间,对象成员变量同步更新;
  2. 如果新的空间使用权size大小大于原对象空间size而小于capacity,则未利用的空间填充value
    ,需要先扩大容器,则会扩容直到可以存储我们的新对象大小,然后使用给定的value去填充未被使用的空间,其容器大小和空间使用权会更新;
  3. 如果需要缩小

resize()、(resize内含reserve)代码演示如下:

    void test3()
    {
        cout << "测试resize()" << endl;

        vector<int*> v1;
        v1.resize(10);

        vector<string> v2;
        v2.resize(10,"xxx");
        
        cout << "v1" << endl;
        for (auto e : v1)
        {
            cout << e << " ";
        }

        cout << endl;
        cout << "v2" << endl;
        for (auto e : v2)
        {
            cout << e << " ";
        }
        cout << endl;
        cout << endl;
    }

在这里插入图片描述

3. vector的尾删函数: vector_name.pop_back();

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

代码演示如下:

void test1()
{
    cout << "试验push_back" << endl;

    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(5);

    for (size_t i = 0; i < v.size(); i++)
    {
        cout << v[i] << " ";
    }
    cout << endl;

    cout << "试验迭代器" << endl;
    vector<int>::iterator it = v.begin();
    while (it != v.end())
    {
        cout << *it << " ";
        it++;
    }
    cout << endl;

    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;
    cout << endl;

    cout << "试验pop_back" << endl;

    v.pop_back();
    v.pop_back();
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;
    cout << endl;

}

在这里插入图片描述

4. vector的删除函数: vector_name.erase(size_type);

        iterator erase(iterator pos)
        {
            assert(pos>=_start);
            assert(pos<_finish);

            iterator it = pos + 1;
            while (it < _finish)
            {
                *(it - 1) = *it;
                ++it;
            }
            --_finish;
            return pos;
        }

代码演示如下:

void test5()
{
    cout << "测试erase()" << endl;
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    v.push_back(5);
    v.push_back(6);
    v.push_back(7);
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    cout << "头删" << endl;
    v.erase(v.begin());
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    cout << "中间删5" << endl;
    v.erase(v.begin() + 3);
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    cout << "尾删" << endl;
    v.erase(v.end() -1);
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;
    cout << endl;
}

在这里插入图片描述

八、vector的默认构造

默认构造函数、拷贝构造函数、赋值运算符重载、取地址及const取地址运算符重载、析构函数。

为了避免过耦合,下面我会把各个功能分开实现;

1. 默认构造函数:vector vector_name();

创建一个空的vector对象,其中T是vector中元素的类型。

  //构造函数
        vector()
            //:_start(nullptr)
            //, _finish(nullptr)
            //, _endofstorage(nullptr)
        {}

2. 带有初始大小的构造函数:vector vector_name(size_type n);

创建一个包含n个默认初始化元素的vector对象。

3. 带有初始值和大小的构造函数:vector vector_name(size_type n, const T& value);

创建一个包含n个值为value的元素的vector对象。
想要实现该功能必须先实现push_back()、reserve();
而想要实现push_back()就必须实习insert();

        iterator insert(iterator pos, const T& x)
        {
            assert(pos >= _start);
            assert(pos <= _finish);

            if (_finish == _endofstorage)
            {
                size_t len = pos - _start;
                reserve(capacity() == 0 ? 4 : capacity() * 2);
                pos = _start+len;   //扩容后pos失效,在此更新
            }

            iterator end = _finish - 1;
            while (end >= pos)  //挪动数据为插入腾出地方
            {
                *(end + 1) = *end;
                --end;
            }
            *pos = x;
            ++_finish;

            return pos;
        }



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



		vector(int n, const T& value = T())
            //:_start(nullptr)
            //,_finish(nullptr)
            //,_endofstorage(nullptr)
        {
            reserve(n);
            for (size_t i = 0; i < n; i++)
            {
                push_back(value);
            }
        }

4. 带有迭代器范围的构造函数:vector vector_name(InputIterator first,InputIterator last);

创建一个包含[first, last)范围内元素的vector对象,其中InputIterator是迭代器类型.

        template<class InputIterator>
        vector(InputIterator first, InputIterator last)
        //vector(size_t n, const T& value = T())面对v(int,int)时会出现混淆的问题
        //所以上面n的类型点名写成int,单独写一个int重载版本
            //:_start(nullptr)
            //, _finish(nullptr)
            //, _endofstorage(nullptr)
        {
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }

1-3函数代码演示如下:

void test7()
{
    cout << "测试各种初始化()" << endl;

    vector<string> v1(10, "xxxx");
    for (size_t i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;
    for (auto e:v1)
    {
        cout << e << " ";
    }
    cout << endl;

    vector<int> v2(10, 1);
    for (size_t i = 0; i < v1.size(); i++)
    {
        cout << v2[i] << " ";
    }
    cout << endl;
    for (auto e : v2)
    {
        cout << e << " ";
    }
    cout << endl;


    cout << endl;
    cout << endl;
}

在这里插入图片描述

5. 拷贝构造函数:vector vector_name_1(vector_name_2);

        //拷贝构造C        
        vector(const vector<T>& v)
            //:_start(nullptr)
            //, _finish(nullptr)
            //, _endofstorage(nullptr)
        {
            reserve(v.capacity());
            for (auto& e : v) 
            {
                push_back(e);
            }
        }

代码演示如下:

    void test6()
    {
        cout << "测试拷贝构造()" << endl;
        vector<int> v1;
        v1.push_back(1);
        v1.push_back(2);
        v1.push_back(3);
        for (auto e : v1)
        {
            cout << e << " ";
        }
        cout << endl;


        vector<int> v2(v1);
        for (auto e : v2)
        {
            cout << e << " ";
        }
        cout << endl;

        vector<int> v3;
        v3 = v1;
        for (auto e : v1)
        {
            cout << e << " ";
        }
        cout << endl;
        cout << endl;
    }

在这里插入图片描述

6. 赋值运算符重载:vector_name_1=vector_name_2;

//想要实现赋值运算符重载,必须先实现swap()函数

        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> tmp)
        {
            swap(tmp);
            return *this;
        };	

7. 析构函数: ~vector()

        //析构函数
        ~vector()
        {
            delete[] _start;
            _start = _finish = _endofstorage = nullptr;
        }

8. const取地址运算符重载: vector_name[size_type n]

        T& operator[](size_t pos)
        {
            assert(pos < size());
            return _start[pos];
        }
        
  //前面的const:返回值不能改动;后面的const:指定const对象调用该函数
        const T& operator[](size_t pos) const
        {
            assert(pos < size());
            return _start[pos];
        }

9. find()寻找元素

vector里面没有自己的find,在算法库里面有通用的find模板。
只有string的find是自己自带的,其专门针对字符与字符串的情况;

        vector<int> vv(10,0);
        cout<<find(vv.begin(),vv.end(),1);

代码演示如下:


   void test8()
   {
       cout << "find()" << endl;
       vector<int> vv(10,0);
       cout<<find(vv.begin(),vv.end(),1);
   }

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AcWing---公约数---最大公约数

4199. 公约数 - AcWing题库 思路&#xff1a; 最大整数x一定是最大公约数的因数&#xff0c;所以先用__gcd(a,b)求出a和b的最大公因数&#xff0c;再用O(log(n))的算法求出最大公因数的因数&#xff0c;放到vector中&#xff0c;并将vector排序。利用STL中的upper_bound(res.…

模组网络通用丨蜂窝网络基础知识介绍

在物联网的时代&#xff0c;蜂窝网络成为了连接各种智能设备的重要基础。而在蜂窝网络中&#xff0c;蜂窝模组则是实现物联网连接的关键组件。作为物联网开发人员&#xff0c;了解蜂窝网络的基础知识是非常重要的。本文详细解答了6个在开发过程的常见问题&#xff0c;帮助客户更…

自然语言处理NLP概述

大家好&#xff0c;自然语言处理(NLP)是计算机科学领域与人工智能领域中的一个重要方向&#xff0c;其研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。本文将从自然语言处理的本质、原理和应用三个方面&#xff0c;对其进行概述。 一、NLP的本质 NLP是一种…

linux常用目录结构(目录命令)--6986字详谈

前面与大家讨论了linux的发展与由来&#xff08;这一块挺多的&#xff0c;小编还没有编写完成&#xff0c;希望大家理解&#xff09;&#xff0c;紧接着谈到了vmware安装及运行所存在的故障&#xff08;鉴定错误&#xff0c;虚拟机没有网&#xff0c;蓝屏等常见现象的总结及处理…

Mysql5.7 yum 简单/快速安装

Centos7下MySql安装及配置过程&#xff0c;简单直装版 目录 操作步骤 一、检查linux是否已安装MySql二、清除MySQL&#xff08;适用重新安装&#xff09; 1、删除MySQL及其依赖包2、查询遗留的目录3、删除遗留的目录三、开始安装MySQL 1、下载并添加库2、安装MySQL包3、设置My…

Qt环形颜色选择控件, 圆环颜色选择器

参考文章Qt编写自定义控件&#xff1a;环形颜色选择控件_qconicalgradient圆环渐变-CSDN博客 感谢作责提供的方法&#xff0c;下面程序的基础思路同参考文章。 为了更方便使用&#xff0c;这个选择器是基于64色表的&#xff0c;会显示选中的索引和色值。颜色选择时计算方式也…

深入理解 Pandas 中的 groupby 函数

groupby 函数是 pandas 库中 DataFrame 和 Series 对象的一个方法&#xff0c;它允许你对这些对象中的数据进行分组和聚合。下面是 groupby 函数的一些常用语法和用法。 对于 DataFrame 对象&#xff0c;groupby 函数的语法如下&#xff1a; DataFrame.groupby(byNone, axis0…

面试(03)————多线程和线程池

一、多线程 1、什么是线程?线程和进程的区别? 2、创建线程有几种方式 &#xff1f; 3、Runnable 和 Callable 的区别&#xff1f; 4、如何启动一个新线程、调用 start 和 run 方法的区别&#xff1f; 5、线程有哪几种状态以及各种状态之间的转换&#xff1f; 6、线程…

内网穿透的应用-如何在Android Termux上部署MySQL数据库并实现无公网IP远程访问

文章目录 前言1.安装MariaDB2.安装cpolar内网穿透工具3. 创建安全隧道映射mysql4. 公网远程连接5. 固定远程连接地址 前言 Android作为移动设备&#xff0c;尽管最初并非设计为服务器&#xff0c;但是随着技术的进步我们可以将Android配置为生产力工具&#xff0c;变成一个随身…

(十一)RabbitMQ及SpringAMQP

1.初识MQ 1.1.同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&#xff0c;…

YOLOV9 + 双目测距

YOLOV9 双目测距 1. 环境配置2. 测距流程和原理2.1 测距流程2.2 测距原理 3. 代码部分解析3.1 相机参数stereoconfig.py3.2 测距部分3.3 主代码yolov9-stereo.py 4. 实验结果4.1 测距4.2 视频展示 相关文章 1. YOLOV5 双目测距&#xff08;python&#xff09; 2. YOLOv7双目…

第十四届蓝桥杯C/C++大学B组题解(一)

1、日期统计 #include <bits/stdc.h> using namespace std; int main() {int array[100] {5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7,5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5, 8, 6, 1, 8, 3, 0, 3, 7, 9,2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, 9, 4, 4, 6,…

【第十九篇】使用BurpSuite实现XXE+点击劫持(实战案例)

XXE XXE漏洞的原理:攻击者通过注入特殊的XML实体来引用外部资源,比如本地文件系统中的文件。从而读取服务器上的敏感文件。 【1】Burp主动扫描 将条目发送至主动扫描: 仪表盘扫描出XML注入漏洞: 【2】手动测试 原请求包如下: 添加Payload并将 XML 中的数据值替换为我们…

多功能调解室sip可视对讲方案

多功能调解室sip可视对讲方案 人民调解委员会是依法设立的调解民间纠纷的群众性组织。 我国基层解决人民内部纠纷的群众性自治组织.人民调解委员会在城市以居民委员会为单位,农村以村民委员会为单位建立.其任务是: 及时发现纠纷,迅速解决争端.防止矛盾激化,预防,减少犯罪的发生…

EChart简单入门

echart的安装就细不讲了&#xff0c;直接去官网下&#xff0c;实在不会的直接用cdn,省的一番口舌。 cdn.staticfile.net/echarts/4.3.0/echarts.min.js 正入话题哈 什么是EChart&#xff1f; EChart 是一个使用 JavaScript 实现的开源可视化库&#xff0c;Echart支持多种常…

postgresql数据库|数据整合的好工具--Oracle-fdw的部署和使用

概述 Oracle_fdw 是一种postgresql外部表插件&#xff0c;可以读取到Oracle上面的数据。是一种非常方便且常见的pg与Oracle的同步数据的方法 Oracle_fdw 适用场景&#xff1a; Oracle_fdw 是一个开源的 Foreign Data Wrapper (FDW)&#xff0c;主要用于在 PostgreSQL 数据库中…

【2024】Rancher的安装与介绍

———————————————————————————— 记录一下rancher的学习与使用过程 本部分内容包括rancher的介绍、特点、与k8s关系和部署等内容 ———————————————————————————— Rancher是什么&#xff1f; 简单来说&#xff0c;Ranc…

Jackson 2.x 系列【13】特征配置篇之 DeserializationFeature

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 1. 前言2. 值处理2.1 USE_BIG_DECIMAL_FOR_FLOATS2.2 USE_BIG_INTEGER_FOR_INTS2…

Qt QML的插件(Qt Quick 2 Extension Plugin)方法

Qt Quick的插件方法 序言环境前置注意概念——Qt Quick插件的相关知识模块名的相关知识模块名本身注意事项模块名版本注意事项 以示例来说明创建插件qmltypes的生成qmltypes的可能性失效 插件的编码注意1、插件模块版本控制2、pro里的注意 调用插件插件信息输入 序言 网上有很…

清明作业 c++

1.封装一个类&#xff0c;实现对一个数求累和阶乘质数 #include <iostream>using namespace std; int mproduct(int a){if(a>1){return a*mproduct((a-1));}else{return 1;} } class number{int a; public:number():a(5){};number(int a):a(a){}void set(int a){thi…