STL—vector

vector介绍

在C++标准库中,vector是一个常用的序列式容器(线性结构),它就好比c语言中的数组,但是vector有一些数组没有的功能,是一个封装好了的类。

想要使用vector需要先引入头文件:

#include<vector>

要使用vector的一些泛型算法需要引入头文件:

#include<algorithm>

vector底层实现

vector底层数据结构是一个动态开辟的一维数组

其内部含有三个指针(start,finish,end_of_storage)

vector的使用

初始化vector

vector<int> a;

初始化一个装有int类数据的容器,size为0

vector<int> a(10);

初始化一个装有int类数据的容器,size为10,每个数据的默认值为0

vector<int> a(10,-1);

初始化一个装有int类数据的容器,size为10,每个数据初始化为-1

vector<int> b;

vector<int> a(b);

先初始化一个装有int类数据容器b

再使用容器b给装有int类数据容器a初始化

vector<int> b(5,2);

vector<int>a(b.begin(),b.begin()+3);

先初始化一个装有int类数据容器b,size为5,每个数据初始化为2

再用容器b的0-2下标数据(共3个)给装有int类数据容器a初始化,size为3

vector的一些内置函数

vector<int> a;
a.front();  //返回容器第一个元素的值
a.back();   //返回容器最后一个元素的值
a.begin();  //返回指向容器第一个元素的迭代器
a.end();  //返回指向容器最后一个元素下一个位置的迭代器
a[i];  //返回容器第i下标的值
a.clear();  //清空容器的值(size=0, capacity不变)
a.empty();  //判空,如果容器为空则返回true,不为空返回false

vector迭代器iterator的使用

vector<int> a(10,2);

vector<int>::iterator it = a.begin();

初始化一个装有整形数据的容器a,size=10,每个数据初始化为2

初始化指向int类数据的迭代器it,并指向a的第一个元素

it.begin()

返回所指向容器的第一个元素位置的迭代器

it.end()

返回指向最后一个元素下一个位置的迭代器

it.rbegin()

返回指向最后一个元素位置的迭代器

it.rend()

返回指向第一个元素前一个位置的迭代器

vector增删查改

查:

vector find( vector begin,vector end,int val ):在begin到end范围查找val

begin:查找范围起始位置的迭代器

end:查找范围终止位置的迭代器(查找的数包括这一个数)

val:要查找的数值

返回值:如果找到,返回指向此数的迭代器

如果没有找到,返回此容器的end()【指向最后一个元素的下一个位置的迭代器】

查找范围内存在的数

查找范围内不存在的数

增:

尾插:push_back( int a ):尾部插入元素a

指定位置之前插入:insert( 指向此位置的迭代器,int a ):

返回值:新插入位置的迭代器

typedef vector<int> _vector;
typedef vector<int>::iterator _it;

_vector _insert()
{
    _vector a;
    for (int i = 0; i < 5; i++)
    {
        a.push_back(i);
    }
    return a;
}

void print(_vector a,_it it)
{
    for (it = a.begin(); it != a.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}
int main()
{
    _vector a;
    a = _insert();
    _it it = a.begin();
    print(a,it);
    it = find(a.begin(),a.end(),3);
    a.insert(it, 10);   //在容器内3的前面插入数据10
    print(a, it);
    return 0;
}

运行结果:

insert插入原理:

增容

改变size和capacity大小的vector内置函数

void resize( int newsize,int value ):重新设置容器的size

newsize:新设置的大小(小于原本的size则删除原本多出来的数据,大于原本的size则)

value:如果newsize>原本的size,则将多出来的空间赋值为value,如果不写默认是0

void reserve(int newstorage):重新设置容器的容量大小

newstorage:新的容量大小(如果小于原本大小就不进行任何操作)

vs的增容机制

总结:vs中vector增容按照1.5倍增容,Linux中vector增容按照2倍增容

增容的底层原理

删:

void pop_back():尾删

iterator arease( const_iterator first,const_iterator end ):删除范围内的元素(在内部erase会让迭代器++一次)

first:删除起始位置的迭代器

end:删除终止位置的迭代器

注意:如果只传一个迭代器,则只此位置的元素

返回值:删除后下一个元素的迭代器

错误演示:

在循环中使用erase的正确写法

erase删除原理

改:

通过[ ]运算符来进行修改,例如:

vector迭代器失效问题

迭代器的作用是让算法不用关心底层的数据 ,其底层实际就是一个指针。因此迭代器失效就是迭代器底层的指针指向的空间被销毁了,去使用一块已经被释放的空间,结果就是程序崩溃

在vector中,当我们在循环中使用push_back和erase时容易发生迭代器失效问题(即底层指针指向被释放的空间还依然去使用这个指针)

我们分别举两个例子来带大家了解迭代器失效问题。

erase删除导致的迭代器失效

我们在循环中去删除容器中的一个值

我们先给出错误代码

#include<iostream>
#include<vector>
using namespace std;

typedef vector<int> _vector;
typedef vector<int>::iterator _it;

int main()
{
    _vector a;
    for(int i =0;i<10;i++)
    {
        a.push_back(i);
    }
    _it it = a.begin();
    for (it; it != a.end();)
    {
        if (*it == 4)    //删除容器中的4
        {
            a.erase(it);  //没有去接收erase的返回值
        }
        else
        {
            it++;
        }
    }
    return 0;
}

在for循环中,当我们调用erase去删除2时,删除完成后没有用迭代器去接收erase的返回值(2的下一个元素的迭代器),此时迭代器不知道指向的哪里,此时若对迭代器进行操作会导致程序崩溃,也就是迭代器失效

如何避免erase导致的迭代器失效:

a.erase(it) 改为 it = a.erase(it)

insert插入导致的迭代器失效

我们在循环中去向容器中的一个元素前新插入一个值

我们先给出错误代码:

#include<iostream>
#include<vector>
using namespace std;

typedef vector<int> _vector;
typedef vector<int>::iterator _it;

int main()
{
    _vector a;
    for (int i = 0; i < 10; i++)
    {
        a.push_back(i);
    }
    _it it = a.begin();

    for (it; it != a.end();)
    {
        if (*it == 4)  //在元素4前新插入一个数15
        {
            a.insert(it, 15);
        }
        else
        {
            it++;
        }
    }
    return 0;
}

在for循环中,当我们调用insert在4前新插入元素时,插入完成后没有用迭代器去接收insert的返回值(新插入元素的下一个元素的迭代器),此时迭代器不知道指向的哪里,此时若对迭代器进行操作会导致程序崩溃,也就是迭代器失效

如何避免insert导致的迭代器失效:

a.insert(it,15) 改为 it = a.insert(it,15)

模拟实现vector

私有成员:

_iterator _start;
_iterator _finish;
_iterator _end_of_storage;

成员函数:

My_vector();
My_vector(int n,const T& val);
My_vector(otheriterator start,otheriterator finish);
My_vector(const My_vector<T>& v);
My_vector<T>& operator=( My_vector<T> v );

迭代器相关:

begin();
end();
rbegin();
rend();

容量大小相关:

size();
capacity();
resize( int newsize,const T& val = T() );
reserve( int newstorage );

元素访问:

T& front();
T& back();
T& operator[]( const int index );

元素修改:

push_back( const T& val );
pop_back();
insert( _iterator pos,const T& val );
erase( _iterator pos );

整体函数实现:

#include<iostream>
#include<stdlib.h>
#include<assert.h>
using namespace std;

template<class T>
class My_vector
{
public:
    //vector的迭代器是原生态的指针
    typedef T* _iterator;
    typedef const T* _const_iterator;
private:
    _iterator _start;
    _iterator _finish;
    _iterator _end_of_storage;
public:
/*-------------------------------------类的默认成员函数--------------------------------------------*/
    //缺省构造
    My_vector() :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {}
    //构造
    My_vector(int n, const T& val = T()) :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
    {
        _start = new T[n];
        for (int i = 0; i < n; i++)
        {
            _start[i] = val;
        }
        _finish = _start + n;
        _end_of_storage = _finish;
    }
    
    //部分拷贝构造
    template<class otheriterator>
    My_vector(otheriterator start, otheriterator finish)
    {
        int size = 0;
        otheriterator it = start;
        while (it != finish)
        {
            ++it;
            ++size;
        }

        _start = new T[size];
        int i = 0;
        while(i < size)
        {
            _start[i] = *start;
            ++start;
            ++i;
        }
        _finish = _start + size;
        _end_of_storage = _finish;
    }

    //拷贝构造
    My_vector(const My_vector<T>& v):_start(new T[v.size()])
    {
        int size = v.size();
        for (int i = 0; i < size; i++)
        {
            _start[i] = v[i];
        }
        _finish = _start + size;
        _end_of_storage = _finish;
    }

    //重载=
    My_vector<T>& operator=(My_vector<T> v)
    {
        _start = new T[v.size()];
        for (int i = 0; i < v.size(); i++)
        {
            _start[i] = v[i];
        }
        _finish = _start + v.size();
        _end_of_storage = _finish;
        return *this;
    }

    //析构函数
    ~My_vector()
    {
        if (_start)
        {
            delete[]_start;
            _start = nullptr;
            _finish = nullptr;
            _end_of_storage = nullptr;
        }
    }

/*-------------------------------------迭代器相关------------------------------------------*/
    //获取容器起始位置的迭代器
    _iterator begin() { return _start; }
    _const_iterator begin() const { return _start; }

    //获取容器最后一个元素的下一个位置的迭代器
    _iterator end() { return _finish; }
    _const_iterator end() const { return _finish; }

    //获取容器逆向起始位置的迭代器
    _iterator rbegin() { return _finish - 1; }
    _const_iterator rbegin() const { return _finish - 1; }

    //获取容器逆向最后一个元素的前一个位置的迭代器
    _iterator rend() { return _start - 1; }
    _const_iterator rend() const { return _start - 1; }

/*-------------------------------------容量相关------------------------------------------*/
    //获取容器size大小
    int size() const { return _finish - _start; }
    
    //获取容器容量大小
    int capacity() const { return _end_of_storage - _start; }
    
    //判空
    bool empty() const { return _start == _finish; }

    //重新设置size
    void resize(int newsize, const T& val = T())
    {
        int oldsize = this->size();
        if (newsize <= oldsize)
        {
            _finish = _start + newsize;
        }
        else
        {
            reserve(newsize);
            while (_finish != _end_of_storage)
            {
                *_finish = val;
                ++_finish;
            }
        }
    }

    //重新设置容量
    void reserve(int newstorage)
    {
        int oldstorage = this->capacity();
        if (newstorage > oldstorage)
        {
            T* new_start = new T[newstorage];
            if (new_start)
            {
                int size = this->size();
                int i = 0;
                while (i < size)
                {
                    new_start[i] = _start[i];
                    i++;
                }
                delete[]_start;
                _start = new_start;
                _finish = _start + size;
                _end_of_storage = _start + newstorage;
            }
        }
    }

/*-------------------------------------元素访问------------------------------------------*/
    //获取容器第一个元素
    T& front() { return *_start; }
    const T& front() const { return *_start; }

    //获取容器最后一个元素
    T& back() { return *(_finish - 1); }
    const T& back() const { return *(_finish - 1); }

    //重载[]
    T& operator[](const int index)
    {
        if (index < size())
        {
            return _start[index];
        }
    }
    const T& operator[](const int index) const
    {
        if (index < size())
        {
            return _start[index];
        }
    }

/*-------------------------------------元素修改------------------------------------------*/
    //尾插
    void push_back(const T& val)
    {
        int size = this->size();
        if (size == capacity())
        {
            if (size == 0)
            {
                reserve(1);
            }
            else if (size == 1)
            {
                reserve(2);
            }
            else
            {
                reserve(size * 1.5);
            }
        }
        *_finish = val;
        ++_finish;
    }

    //尾删
    void pop_back()
    {
        if (!empty())
        {
            --_finish;
        }
    }

    //任意位置插入
    _iterator insert(_iterator pos, const T& val)
    {
        //插入位置是否合法
        assert(!(pos < _start || pos > _finish));
        //是否需要扩容
        int size = this->size();
        if (size == capacity())
        {
            if (size == 0)
            {
                reserve(1);
            }
            else if (size == 1)
            {
                reserve(2);
            }
            else
            {
                reserve(size * 1.5);
            }
        }
        //插入位置及以后元素全部向后移动一位
        _iterator it = _finish;
        while (it != pos)
        {
            *it = *(it - 1);
            --it;
        }
        //插入元素
        *it = val;
        //__finish也后移一位
        _finish++;
        //返回新插入元素的迭代器
        return pos;
    }

    //删除元素
    _iterator erase(_iterator pos)
    {
        //删除位置是否合法
        assert(!this->empty() && !(pos < _start || pos >= _finish));
        _iterator it = pos;
        //删除位置之后位置向前移动一位
        while (it != _finish)
        {
            *(it - 1) = *it;
            ++it;
        }
        //并且_finish也向前移动一位
        --_finish;
        //返回删除元素的下一个元素的迭代器
        return pos;
    }

    void clear()
    {
        _finish = _start;
    }
};

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

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

相关文章

【C陷阱与缺陷】----语法陷阱

&#x1f4af;&#x1f4af;&#x1f4af; 要理解一个C程序&#xff0c;必须理解这些程序是如何组成声明&#xff0c;表达式&#xff0c;语句的。虽然现在对C的语法定义很完善&#xff0c;几乎无懈可击&#xff0c;大门有时这些定义与人们的直觉相悖&#xff0c;或容易引起混淆…

【机器学习】综述:机器学习中的模型评价、模型选择与算法选择

文章目录一、前言二、论文摘要三、简介&#xff1a;基本的模型评估项和技术3.1 性能评估&#xff1a;泛化性能 vs. 模型选择四、Bootstrapping 和不确定性五、交叉验证和超参数优化一、前言 最近在做实验的时候&#xff0c;发现树模型有过拟合的情况发生&#xff0c;为此&…

蓝桥杯每日一真题—— [蓝桥杯 2021 省 AB2] 完全平方数(数论,质因数分解)

文章目录[蓝桥杯 2021 省 AB2] 完全平方数题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1样例 #2样例输入 #2样例输出 #2提示思路&#xff1a;理论补充&#xff1a;完全平方数的一个性质&#xff1a;完全平方数的质因子的指数一定为偶数最终思路&#xff1a;小插曲&am…

直面风口,未来不仅是中文版ChatGPT,还有AGI大时代在等着我们

说到标题的AI2.0这个概念的研究早在2015年就研究起步了&#xff0c;其实大家早已知道&#xff0c;人工智能技术必然是未来科技发展战略中的重要一环&#xff0c;今天我们就从AI2.0入手&#xff0c;以GPT-4及文心一言的发布为切入角度&#xff0c;来谈一谈即将降临的AGI时代。 关…

Linux搭建GitLab私有仓库,并内网穿透实现公网访问

文章目录前言1. 下载Gitlab2. 安装Gitlab3. 启动Gitlab4. 安装cpolar5. 创建隧道配置访问地址6. 固定GitLab访问地址6.1 保留二级子域名6.2 配置二级子域名7. 测试访问二级子域名前言 GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用Git作为代码管理工具&#xff0c…

基于Springboot实现商务安全邮箱邮件收发 源码+论文展示

基于Springboot实现商务安全邮箱邮件收发 源码论文开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Ma…

【多线程】定时器和线程池

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; ✨每日一语&#xff1a;种一棵树最好的时间是十年前&#xff0c;其次是现在。 目 录⌚️一. 定时器&#x1f4c4;1. 定时器是什么&#x1f4c3;2. 标准库中的定时器&#x1f4d1;3.…

【C语言初阶】初识C语言 | C语言知识预览

文章目录&#x1f490;专栏导读&#x1f490;文章导读&#x1f337;什么是C语言&#xff1f;&#x1f337;第一个C语言程序&#x1f337;数据类型&#x1f337;变量、常量、字符串&#x1f33a;定义变量的方法&#x1f33a;变量的分类&#x1f33a;变量的使用&#x1f33a;变量…

DJ2-5 DNS:Internet 的目录服务

目录 1. DNS 简介 2. DNS 服务器提供的功能 3. 分布式、层次数据库 4. DNS 查询方法 5. DNS 缓存和权威 DNS 服务器记录更新 6. DNS 记录 7. DNS 报文 8. 在 DNS 数据库中插入记录 9. DNS 攻击 1. DNS 简介 名称&#xff1a;Domain Name System DNS 是&#xff1a; …

vue面试题(day06)

文章目录前言请谈谈WXML与标准的html的异同&#xff1f;请谈谈WXSS和CSS的异同&#xff1f;请谈谈微信小程序主要目录和文件的作用&#xff1f;请谈谈小程序的双向绑定和vue的异同&#xff1f;简单描述下微信小程序的相关文件类型&#xff1f;微信小程序有哪些传值(传递数据)方…

【新星计划2023】SQL SERVER (01) -- 基础知识

【新星计划2023】SQL SERVER -- 基础知识1. Introduction1.1 Official Website1.2 Conn Tool2. 基础命令2.1 建库建表2.2 Alter2.3 Drop2.3 Big Data -- Postgres3.Awakening1. Introduction 1.1 Official Website 官方文档&#xff08;小技巧&#xff09; Officail Website: …

十个Python图像处理工具,不可不知

这些Python库提供了一种简单直观的方法来转换图像并理解底层数据。 今天的世界充满了数据&#xff0c;图像是这些数据的重要组成部分。但是&#xff0c;在使用它们之前&#xff0c;必须对这些数字图像进行处理 - 分析和操作&#xff0c;以提高其质量或提取一些可以使用的信息。…

【C++学习】继承

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《C学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; C是面向对象的编程语言&#xff0c;它有很多的特性&#xff0c;但是最重要的就是封装&#xff0c;继承…

【3DoF算法】

VR 3DoF算法介绍 核心&#xff1a;3DoF算法应用场景&#xff0c;在VIO应用中&#xff0c;当只有测量没有观测的情况下&#xff0c;6DoF算法的预测会退化成一个只有测量的3DoF算法&#xff0c;这时候需要使用3DoF算法&#xff0c;来更加稳定准确的获取3DoF位姿&#xff0c;直到…

【VSCode】Windows 下搭建 Fortran 环境

文章目录Part.I 预备知识Part.II 安装与配置Chap.I 编译环境Chap.II 插件Part.III 测试Chap.I 一个示例Chap.II 注意事项Part.I 预备知识 Fortran 是一种比较古老的语言了&#xff0c;当时作为一种科学计算工具&#xff0c;还是比较火的&#xff0c;因为很多有名的软件都是基于…

LFM雷达实现及USRP验证【章节2:LFM雷达测距】

目录 1. 参数设计 几个重要的约束关系 仿真参数设计 2. matlab雷达测距代码 完整源码 代码分析 回顾&#xff1a;LFM的基本原理请详见第一章 本章节将介绍LFM雷达测距的原理及实现 1. 参数设计 几个重要的约束关系 带通采样定理&#xff1a; 因此如果我们B80MHz时&a…

SQL优化13连问,收藏好!

1.日常工作中&#xff0c;你是怎么优化SQL的&#xff1f; 大家可以从这几个维度回答这个问题&#xff1a; 分析慢查询日志 使用explain查看执行计划 索引优化 深分页优化 避免全表扫描 避免返回不必要的数据&#xff08;如select具体字段而不是select*&#xff09; 使用…

【Android -- 开发工具】Xshell 6 安装和使用教程

一、简介 Xshell 其实就是一个远程终端工具&#xff0c;它可以将你的个人电脑和你在远端的机器连接起来&#xff0c;通过向 Xshell 输入命令然后他通过网络将命令传送给远端Linux机器然后远端的Linux机器将其运行结果通过网络传回个人电脑。 二、Xshell 6 的安装 首先&#…

如何通过命令行查看CentOS版本信息和linux系统信息

1.如何查看已安装的CentOS版本信息&#xff1a; 1.cat /proc/version 2.uname -a 3.uname -r 4.cat /etc/centos-release 5.lsb_release -a 6.hostnamectl1. 第一种方式输出的结果是&#xff1a; Linux version 3.10.0-1127.el7.x86_64 (mockbuildkbuilder.bsys.centos.org) …

算法基础-回溯算法

回溯算法大致分为以下几类&#xff1a; 组合&#xff1a;组合、组合总和、电话号码的字母组合 分割&#xff1a;分割回文串、复原IP地址 子集&#xff1a;子集 排列&#xff1a;全排列 棋盘问题&#xff1a;N皇后、解数独 其他&#xff1a;递增子序列、重新安排行程 一、什么是…
最新文章