C++初阶:vector类

2023-05-16

vector

0. vector的介绍

vector是用数组实现的、可变长度的顺序容器,本质是一种类模板。

template < 
	class T, // 元素类型
	class Alloc = allocator<T> > // 空间配置器类型

class vector; // 类模板声明

1. vector的接口

1.1 默认成员函数

接口声明解释
vector()默认构造
vecotr(size_type n, const_value_type& val=value_type())填充构造,填充n个元素
vector(InputIter first, InputIter last)范围构造,迭代器区间初始化
vector(const vector& v)拷贝构造
vector& operator=(const vector& x)赋值重载

1.2 容量操作

容量操作解释
size_type size()元素个数
size_type capacity()容量大小
size_type max_size()最大能存储的元素个数(无意义)
void resize(size_type n, value_type val = value_type());增减有效元素个数
v.reserve(100);   // 扩容到100
v.resize(100, 1); // 有效元素个数变为100,新增元素初始化为1
v.resize(10);     // 有效元素个数变为10

由图可知,vs下vector按1.5倍增容。

1.3 访问操作

接口声明解释
reference operator[](size_type n)返回下标位置的引用
const_reference operator[] (size_type n) const
reference at(size_type n)
const_reference at (size_type n) const

[]重载和at的区别是,[]越界会断言报错,at是抛异常。

迭代器接口解释
begin起始位置的迭代器
end末尾元素的下一个位置的迭代器
rbegin反向起始位置的迭代器
rend反向末尾元素的下一个位置的迭代器
cbegin,cendbegin 和 end 的 const 版本

[]重载就已经能方便的访问 vector,但并不意味着放弃迭代器。大部分容器都支持迭代器访问,且迭代器使用简单规范统一。

STL 中容器的迭代器区间都是采用 [ f i r s t , l a s t ) [first,last) [first,last) 左闭右开的方式。

//[]
for (size_t i = 0; i < v.size(); i++) {
    v1[i] += 1;
}

//iterator
vector<int>::iterator it = v.begin();
while (it != v.end()) {
    cout << *it << " ";
    it++;
}

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

1.4 修改操作

接口声明解释
void push_back (const value_type& val)尾插
void pop_back()尾删
iterator insert (iterator pos, const value_type& val)迭代器位置插入
void insert (iterator pos, size_type n, const value_type& val);迭代器位置插入
void insert (iterator pos, InputIter first, InputIter last)迭代器位置插入一段区间
iterator erase (iterator pos)迭代器位置删除
iterator erase (iterator first, iterator last)删除一段迭代器区间
void assign (size_type n, const value_type& val)覆盖数据
v.insert(ret, 30);
v.insert(ret, 2, 30);
v.insert(ret, v2.begin(), v2.end());
v1.erase(pos);
v1.erase(v1.begin(), v1.end());
#include <algorithm>
// 查找接口
template <class InputIter, class T>
   InputIter find (InputIter first, InputIter last, const T& val);

 

2. vector的模拟实现

在这里插入图片描述

2.1 类的定义

template <class T, class Alloc = alloc>
class vector {
public:
    typedef T* iterator;
    // ...
private:
    iterator start;
    iterator finish;gggg
    iterator end_of_storage;
}

这个结构和顺序表结构稍有不同,但本质是一样的。只是将容量和元素个数的变量用指向对应位置的迭代器代替。

class Seqlist {
    T* _a;            /* start */
    size_t _size;     /* finish - start */
    size_t _capacity; /* end_of_storage - start */
}

2.2 默认成员函数

//default constructor
vector()
    : _start(nullptr)
    , _finish(nullptr)
    , _end_of_storage(nullptr)
{}
//fill constructor
vector(size_t n, const T& val = T()) // 引用临时对象可延长其声明周期
    : _start(nullptr)
    , _finish(nullptr)
    , _end_of_storage(nullptr)
{
    resize(n, val);  
}
//copy constructor
vector(const vector<T>& v)
    : _start(nullptr)
    , _finish(nullptr)
    , _end_of_storage(nullptr)
{
    _start = new T[v.capacity()];
    for (size_t i = 0; i < v.capacity(); i++)
    {
        _start[i] = v._start[i];
    }
    _finish = _start + v.size();
    _end_of_storage = _start + v.capacity();
}
//range constructor
template <class InputIterator>
vector(InputIterator first, InputIterator last) 
    : _start(nullptr)
	, _finish(nullptr)
	, _end_of_storage(nullptr)
{
	while (first != last) 
	{
		push_back(*first++);
    }
}
//destructor
~vector() 
{
    delete[] _start;
    _start = _finish = _end_of_storage = nullptr;
}

// 现代写法
//copy constructor
vector(const vector<T>& v) 
    : _start(nullptr)
	, _finish(nullptr)
	, _end_of_storage(nullptr)
{
    vector<T> tmp(v.begin(), v.end());
    swap(tmp);
}
//operator=
vector<T>& operator=(vector<T> v)  /* pass by value */
{
    swap(v);
    return *this;
}

从范围构造可以看出类模板中的函数也可以是函数模板。

迭代器的分类

函数模板的模板参数要传迭代器区间时,命名是有规定的,范围构造中的InputIterator就是一种指定的迭代器类型。因容器的结构各有不同,迭代器分为五种类型:

迭代器类型名称解释适用容器
input/output_iterator输入/输出迭代器只读迭代器只能读取,只写迭代器可以写入该位置的值无实际容器
forward_iterator向前迭代器只能向前移动(++),允许在迭代器位置进行读写forward_list
bidirectional_iterator双向迭代器可以双向移动(++,––),允许在迭代器位置进行读写list, map, set
random_access_iterator随机迭代器支持指针运算,可移动(++,––)任意跳转(+,–)读写(*)deque,vector,string

可以看出,下方的迭代器类型是上方的父类,也就是说下方迭代器满足上方的所有要求

划分出不同的迭代器类型,是为了限制传入的迭代器,因为其必须满足要求才能完成接下来的函数。

函数如果指明迭代器类型为InputIterator,意思是满足输入迭代器的要求的迭代器都可以作此参数。故此处我们可以传入任意的迭代器。

一般底层是数组连续空间的容器,例如 vector, string 等都是随机迭代器。

像双向链表这样的非连续空间的容器是双向迭代器。

2.3 容量接口

memcpy 浅拷贝问题

vector<string> v;
v.push_back("11111111111111");
v.push_back("11111111111111");
v.push_back("11111111111111");
v.push_back("11111111111111");
v.push_back("11111111111111"); // 增容浅拷贝

出现问题是因为正好数组需要增容。模拟实现的reserve函数使用memcpy将原空间的内容按字节拷贝至新空间。

  1. 若 vector 存储的是内置类型,则浅拷贝没问题。
  2. 若 vector 存储的是自定义类型,浅拷贝使得新旧变量指向同一块空间。深拷贝调用拷贝构造或者赋值重载。

void reserve(size_t n) 
{
    if (n > capacity()) 
    {
        T* tmp = new T[n];
        size_t oldSize = size();
        
        if (_start) 
        {
            //memcpy(tmp, _start, size() * sizeof(T)); // err
            for (int i = 0; i < size(); i++) 
            {
                tmp[i] = _start[i];//_start指向的空间存任意类型都能完成深拷贝
            }
            
            delete[] _sta rt;
        }
        
        _start = tmp;
        _finish = _start + oldSize;
        _end_of_storage = _start + n;
    }
}
void resize(size_t n, T val = T())
{
    if (n > size())
    {
        reserve(n);
        while (_finish != _start + n)
        {
            *_finish = val;
            ++_finish;
        }
    }
    else
    {
        _finish = _start + n;
    }
}

2.4 修改接口

iterator insert(iterator pos, const T& val)
{
    assert(_start <= pos && pos <= _finish); // 检查pos位置是否合法
    // 增容
    if (_finish == _end_of_storage) 
    {
        size_t sz = pos - _start;
        reserve(capacity() == 0 ? 4 : capacity() * 2); //增容会导致迭代器失效,迭代器位置陈旧
        pos = _start + sz; //增容后更新pos
    }
    
    // 后移 [pos,_finish)
    for (iterator end = _finish; end > pos; --end)
    {
        *end = *(end - 1);
    }
    
    // 插入
    *pos = val;
    ++_finish;
    return pos; //返回迭代器最新位置
}
  • 增容改变_start,但迭代器pos并没有跟着改变,仍然指向原空间,也就是迭代器失效。
  • 迭代器pos实参并没有改变仍然指向错误位置,故函数返回更新的pos
iterator erase(iterator pos)
{
    assert(_start <= pos && pos < _finish);

    for (iterator begin = pos + 1; begin < _finish; begin++)
    {
        *(begin - 1) = *begin;
    }
    
    _finish--;
    return pos; //返回删除数据的下一个位置
}

  • erase 挪动数据后 pos 指向元素会发生变化,同样会导致迭代器失效。
  • 返回删除数据的下一个位置,通过返回值更新迭代器。

 

3. vector的oj题

题目题解
只出现一次的数字【简洁明了】只出现一次的数字 I
杨辉三角【简洁明了】cpp 杨辉三角
删除有序数组中的重复项【双指针 简单明了】数组去重
只出现一次的数字 II【简单明了 注释】只出现一次的数字
只出现一次的数字 III【详细解析】只出现一次的数字 系列 I II III
多数元素【哈希 排序 投票】三种解法 简洁明了
连续子数组的最大和【动归】连续子数组的最大和
电话号码的字母组合【注释解析 回溯】电话号码字母组合
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++初阶:vector类 的相关文章

  • 如何找到一个向量中与另一个向量最接近的值?

    我有两个大小相等的向量 例如 A 2 29 2 56 2 77 2 90 2 05 and B 2 34 2 62 2 67 2 44 2 52 我有兴趣在两个相同大小的向量 A 和 B 中找到最接近的值 几乎相等 即在 A 中的所有元素中
  • 使用shared_ptr的例子?

    你好 我今天问了一个关于如何在同一个向量数组中插入不同类型的对象 https stackoverflow com questions 3475030 different types of objects in the same vector
  • 交叉连接 2 个向量的元素以生成第三个向量

    我有 2 个向量 想要将一个向量分布到另一个向量上以形成第三个向量 例如 V1 a b c V2 d e f Result V3 ad ae af bd be bf cf nine total elements 我知道如何做到这一点的唯一方
  • 我可以在 Javascript 中定义自定义运算符重载吗? [复制]

    这个问题在这里已经有答案了 是否可以在 JavaScript 中的类型实例之间定义自定义运算符 例如 假设我有一个自定义向量类 是否可以使用 vect1 vect2 检查是否相等 而底层代码会是这样的 operator a b return
  • 如何在 C++ 中创建多个向量的组合而无需硬编码循环?

    我有几个数据看起来像这样 Vector1 elements T C A Vector2 elements C G A Vector3 elements C G T up to VectorK elements Note also that
  • SVG/矢量图室内导航路由

    我一直在网上搜索有关如何为基于 SVG 的室内平面图实现我自己的点对点导航系统的教程或方法 我已经在网上搜索过 但唯一的选项适用于谷歌地图 不过 我使用 Illustrator 创建了地图 并使用路径 矢量作为 SVG 图像 我不需要为用户
  • 释放指针向量,但内存仍在使用中

    我不知道下面的代码有什么问题 我正在删除所有指针 但是当我使用 top 命令查看内存时 我可以看到仍然有大量内存分配给程序 我在这里缺少一些东西来释放内存吗 include
  • 如何在 C++ 中将值从向量转换为映射?

    我想做这样的事情 有没有一个stl算法可以轻松做到这一点 for each auto aValue in aVector aMap aValue 1 如果您有一个对向量 其中对中的第一项将是映射的键 第二项将是与该键关联的值 您可以使用插入
  • rbind 命名向量到不同长度的矩阵

    我正在尝试将命名向量绑定到矩阵上 命名向量的长度与矩阵不同 gt m lt matrix data c 1 2 3 nrow 1 ncol 3 dimnames list c c column 1 column 2 column 3 gt
  • XNA 2D 矢量角度 - 正确的计算方法是什么?

    在 2D 中的 XNA 中矢量角度的标准工作方式是什么 向右 0 度 向上 90 度 向左 180 度 向下 270 度 什么是 标准 实现 float VectortoAngle Vector2 vec and Vector2 Angle
  • 将 R 中的向量按特定顺序转换为下三角矩阵

    我有一个向量 其中元素的顺序很重要 比如说 x lt c 1 2 3 4 我想将我的向量排列成具有特定顺序的下三角矩阵 其中每行包含向量的前一个元素 我的目标是获得以下矩阵 lower diag matrix 1 2 3 4 1 4 0 0
  • 矩阵向量变换

    我正在编写一个代码来制作软件蒙皮器 骨骼 皮肤动画 并且我正处于 优化 阶段 蒙皮器工作得很好 并且在 Core 上 1 09 毫秒内对 4900 个三角形网格与 22 个骨骼进行蒙皮Duo 2 Ghz 笔记本 我需要知道的是 1 有人可以
  • C# 等价于 C++ 向量或双端队列

    我几乎可以肯定这应该是重复的 但我搜索了一段时间但找不到答案 我应该在 C 中使用什么来替换 C 向量和双端队列有效率的 也就是说 我需要一种有效支持直接索引的结构 并且还支持以有效的方式再次从一端或两端删除 取决于向量或双端队列的情况 在
  • 如何在 switch 语句中将向量作为参数传递

    我对问题的谷歌搜索没有返回有用的结果和文档 switch没有告诉我如何做 所以我希望我能在这里得到答案 假设我有一个向量 cases lt c one two three 我想使用 switch 语句并将这些元素作为 switch 语句的参
  • 我应该如何格式化 .dat 文件以便制作 3D 矢量图?

    我正在为大学做这个编程任务 我们必须写一个c 计算 3D 空间中某些线圈的磁场矢量的程序 我已经成功编写了这个程序 并且我认为它运行得很好 不过 我想添加一个特殊的东西 这是我的试卷 所以它必须特别好 我想绘制出向量 我习惯打电话gnupl
  • 如果我不定义向量大小,程序会崩溃

    最近在学习C 偶然发现这个问题 std vector
  • 擦除-删除习惯用法的性能增益来自哪里

    我需要从向量中删除满足特定条件的所有元素 我的第一种方法是循环遍历向量并对满足条件的所有元素调用 vector erase 据我所理解 vector erase对于这个用例来说 性能很差 因为它从底层数组中删除了该项目 并将向量的其余部分向
  • vector 超出范围后不清除内存

    我遇到了以下问题 我不确定我是否错了或者它是一个非常奇怪的错误 我填充了一个巨大的字符串数组 并希望在某个点将其清除 这是一个最小的例子 include
  • 如何在 R 中的 for 循环内将值存储在向量中

    我正在开始使用 R 但我对以下问题感到非常沮丧 我试图将 for 循环内完成的某些计算的值存储到我之前定义的向量中 问题是如何进行索引 因为for循环迭代代码的次数取决于用户的输入 所以变量i不一定要从1开始 它可以从80开始 for举个例
  • 使用 swig 类型映射将向量> & 从 C++ 方法返回到 python 元组列表

    我在尝试包装一个 C 方法时遇到了很多麻烦 该方法将对向量的常量引用返回到 Python 元组列表 typemap out 我目前有这样的事情 myclass h inlcude

随机推荐

  • html的input表单限制纯数字及字符长度

    1 限制字符长度用maxlength属性 2 限制input输入框为纯数字 xff1a a onkeyup 61 34 value 61 value replace d g 39 39 34 使用 onkeyup 事件 xff0c 有 bu
  • ios兼容性问题---position:absolute;属性

    替代方案 xff1a 移动端普遍是锁定视口的 xff0c 可用position fixed 替代
  • 解决ios微信公众号网页现在新增底部前进后退导航栏产生的布局问题

    现象 xff1a 新增前进后退导航栏 问题产生原因 xff1a 新增导航栏使网页脱离文档流的屏幕高度变小 解决方案 xff1a 布局时考虑到影响脱离文档流的底部元素即可根据需要合理布局
  • kali Linux使用蓝牙

    lsusb 运行hciconfig可以看到 xff1a hci0 Type BR EDR Bus USB BD Address 28 B2 BD 4F 6A 63 ACL MTU 8192 128 SCO MTU 64 128 UP RUN
  • 解决请在微信客服端打开链接问题

    现象 xff1a 在浏览器打开链接 xff0c 显示如图 现象原因 xff1a 调用了微信的接口 解决 xff1a 不调用微信的接口即可 应用场景 xff1a 如果需要在浏览器调试微信公众号网页 xff0c 可以模拟调用必要微信接口的信息或
  • vue+axios上传文件的几种方式及步骤(以上传图片为例)

    1 用js的formData对象上传 xff08 服务器返回url地址 xff09 lt input class 61 34 file 34 name 61 34 file 34 type 61 34 file 34 accept 61 3
  • 解决浏览器缩放时导致的页面布局的变化

    现象 xff1a 正常展示 xff1a 缩放展示 xff1a 原因 xff1a 在网页中 xff0c 如果一个元素没有设置最小宽度 min width xff0c 这时当浏览器缩小到一定程度时 xff0c 元素中的布局可能会发生变化 缩放实
  • 机器学习各大算法简单介绍

    这两天把机器学习的一些基础算法的 简单介绍 整理了一下 xff0c 分别都有概念 xff08 基本思想 xff09 优点 缺点 基本上都是在网络上各个文章里摘录的 xff0c 所以内容也不算我原创 xff0c 但是我把它们筛选整理在了一起
  • Github for windows 使用教程(二)

    转载请注明出处 GitHub for windows使用教程 xff08 二 xff09 分支的使用 创建分支 我们创建第一个分支取名为 new masterh 点击Create new branch创建第一个分支 我们发现此时的分支已经切
  • 「leetcode」C++题解:239. 滑动窗口最大值,单调队列的经典题目

    更多精彩文章持续更新 xff0c 微信搜索 代码随想录 第一时间围观 xff0c 本文https github com youngyangyang04 TechCPP 已经收录 xff0c 里面有更多干货等着你 xff0c 欢迎Star x
  • BAT程序员总结的力扣刷题指南,已经在Github了!!刷题顺序,优质题解一网打尽!

    相信很多小伙伴刷题的时候面对力扣上近两千道题目 xff0c 感觉无从下手 xff01 我花费半年时间整理了Github学习项目 力扣刷题攻略 xff1a https github com youngyangyang04 leetcode m
  • 扩展卡尔曼滤波EKF与多传感器融合

    Extended Kalman Filter xff08 扩展卡尔曼滤波 xff09 是卡尔曼滤波的非线性版本 在状态转移方程确定的情况下 xff0c EKF已经成为了非线性系统状态估计的事实标准 本文将简要介绍EKF xff0c 并介绍其
  • 我把年终总结写成了年度回忆录(1)

    写在前面 这大概是我第一次正儿八经的写个年终总结 xff0c 其实 xff0c 更像是一篇很有意思的回忆录 去年元旦的情景我已经记不起来了 但这一年里 xff0c 却是有很多事情让我难以忘记 从去年寒假自己在出租屋里苦学的时光 xff0c
  • .mat文件后缀名消失

    情况说明 xff1a 下载了 mat文件后 xff0c 打开文件发现文件的后缀名缺失了 xff0c 并且文件类型变为Microsoft Access Table Shortcut类型 具体原因 xff1a 这是由于MATLAB和Access
  • lsusb命令

    在 Linux 中我们可以使用 lsusb 来列出 USB 设备和它的属性 xff0c lsusb 会显示驱动和内部连接到你系统的设备 直接在控制台输入 lsusb 即可 如果无法运行 lsusb xff0c 使用以下命令安装 xff08
  • 现代控制理论基础——卡尔曼滤波(kalman filtering)

    现代控制理论基础 卡尔曼滤波 xff08 kalman filtering xff09 什么是卡尔曼滤波 xff1f 在任何含有不确定信息的动态系统中使用卡尔曼滤波 xff0c 对系统下一步的走向做出有根据的预测 xff0c 对系统状态进行
  • C/C++中的'\0'

    在C C 43 43 语言中 xff0c 字符是按其所对应的ASCII码来存储的 xff0c 一个字符占一个字节 xff0c 而 0 就是ASCII码表中的第一个字符 xff0c ASCII码为00000000 xff0c 它被称为空字符
  • OpenCV 创建图像时,CV_8UC1,CV_32FC3,CV_32S等参数的含义

    形式 xff1a CV lt bit depth gt S U F C lt number of channels gt bit depth xff1a 比特数 代表8bite 16bites 32bites 64bites 举个例子吧 比
  • 解决apt-get update更新错误

    sudo apt get update出现解析错误 xff0c 如下 fkuner 64 data3 span class token function sudo span span class token function apt get
  • C++初阶:vector类

    vector 0 vector的介绍 vector是用数组实现的 可变长度的顺序容器 xff0c 本质是一种类模板 span class token keyword template span span class token operat