【C++ 】-vector:新时代动态数组的革新与未来

目录

1. vector的介绍及使用

1.1 vector的介绍

1.1.1 vector是什么

1.1.2 vector的存储机制

1.2 vector的使用

1.2.1 定义和构造函数

1.2.2 迭代器

1.2.3 容量相关操作

1.2.4 元素访问和修改

1.3 迭代器失效问题

2. vector深度剖析及模拟实现

2.1 std::vector的模拟实现

2.2 memcpy的使用问题

2.3 动态二维数组

总结


 

专栏:C++学习笔记 

上一卷:C++—STL

在C++标准模板库(STL)中,vector是一个非常重要的容器,它提供了一个动态数组,可以根据需要自动调整其大小。

1. vector的介绍及使用

1.1 vector的介绍

1.1.1 vector是什么

vector是一个序列容器,表示可变大小的数组。与数组相似,vector使用连续的存储空间来存储元素,因此可以通过下标访问元素,且效率与数组相当。不同的是,vector的大小是动态的,会随着元素的增加自动调整。

小李的理解vector就像是一个能自动扩展的数组,当需要放更多的东西时,它会自动找更大的地方,把原来的东西搬过去。

1.1.2 vector的存储机制

vector的实现基于动态分配数组。当新元素插入时,如果当前数组容量不足,vector会重新分配一个更大的数组,并将现有元素复制到新数组中。这种重新分配是一个耗时操作,但vector通常会预留额外的空间以减少重新分配的频率,从而优化性能。

小李的理解vector的存储方式就像是搬家,当家里东西太多放不下时,它会找到一个更大的房子,把所有东西搬过去,这样下次再放东西就不用总是搬家了。

1.2 vector的使用

使用vector时,必须熟悉其常用接口。以下是一些重要的接口:

1.2.1 定义和构造函数

  • 无参构造函数:vector()
  • 指定大小和默认值的构造函数:vector(size_type n, const value_type& val = value_type())
  • 拷贝构造函数:vector(const vector& x)
  • 使用迭代器范围的构造函数:vector(InputIterator first, InputIterator last)
#include <vector>
#include <iostream>int main() {std::vector<int> v1; // 默认构造函数std::vector<int> v2(10, 1); // 构造一个包含10个1的vectorstd::vector<int> v3(v2); // 拷贝构造std::vector<int> v4(v2.begin(), v2.end()); // 迭代器范围构造return 0;
}

小李的理解创建vector的方法就像是买不同规格的箱子,有的箱子是空的(无参构造),有的箱子里已经装满了指定数量的物品(指定大小和默认值),有的箱子是完全照搬另一个箱子的东西(拷贝构造),还有的是根据一个范围内的物品来装箱(迭代器范围构造)。

1.2.2 迭代器

  • begin()end():获取首元素和末尾后一个位置的迭代器
  • rbegin()rend():获取末元素和首元素前一个位置的反向迭代器
#include <vector>
#include <iostream>int main() {std::vector<int> v{1, 2, 3, 4, 5};for (auto it = v.begin(); it != v.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}

小李的理解迭代器就像是一个指针,可以帮我们一个一个地访问箱子里的东西,从头到尾,也可以从尾到头。

1.2.3 容量相关操作

  • size():获取当前元素个数
  • capacity():获取当前容量
  • empty():判断是否为空
  • resize():调整大小
  • reserve():预留存储空间
#include <vector>
#include <iostream>int main() {std::vector<int> v;v.reserve(10); // 预留空间v.resize(5); // 调整大小为5std::cout << "Size: " << v.size() << ", Capacity: " << v.capacity() << std::endl;return 0;
}

 

小李的理解size告诉我们箱子里有多少东西,capacity告诉我们箱子能装多少东西,empty告诉我们箱子是不是空的,resize可以调整箱子里的东西数量,reserve可以提前预留空间,避免频繁换箱子。

1.2.4 元素访问和修改

  • operator[]:下标访问
  • at():带边界检查的访问
  • push_back():尾部插入
  • pop_back():尾部删除
  • insert():在指定位置插入
  • erase():删除指定位置的元素
  • swap():交换两个vector的内容
#include <vector>
#include <iostream>int main() {std::vector<int> v{1, 2, 3, 4, 5};v.push_back(6); // 尾部插入std::cout << "After push_back(6): ";for (const auto& elem : v) {std::cout << elem << " ";}std::cout << std::endl; // After push_back(6): 1 2 3 4 5 6v.pop_back(); // 尾部删除std::cout << "After pop_back: ";for (const auto& elem : v) {std::cout << elem << " ";}std::cout << std::endl; // After pop_back: 1 2 3 4 5v.insert(v.begin() + 2, 10); // 在第三个位置插入10std::cout << "After insert at position 2: ";for (const auto& elem : v) {std::cout << elem << " ";}std::cout << std::endl; // After insert at position 2: 1 2 10 3 4 5v.erase(v.begin() + 2); // 删除第三个位置的元素std::cout << "After erase at position 2: ";for (const auto& elem : v) {std::cout << elem << " ";}std::cout << std::endl; // After erase at position 2: 1 2 3 4 5std::vector<int> v2{7, 8, 9};std::swap(v, v2); // 交换内容std::cout << "After swap: v: ";for (const auto& elem : v) {std::cout << elem << " ";}std::cout << std::endl; // After swap: v: 7 8 9std::cout << "v2: ";for (const auto& elem : v2) {std::cout << elem << " ";}std::cout << std::endl; // v2: 1 2 3 4 5return 0;
}

解释

  • v.push_back(6)vector末尾插入6,结果为1 2 3 4 5 6
  • v.pop_back()删除vector末尾元素,结果为1 2 3 4 5
  • v.insert(v.begin() + 2, 10)在第三个位置插入10,结果为1 2 10 3 4 5
  • v.erase(v.begin() + 2)删除第三个位置的元素,结果为1 2 3 4 5
  • std::swap(v, v2)交换vv2的内容,v变为7 8 9v2变为1 2 3 4 5

 

 

小李的理解vector就像是一个万能的箱子,不仅可以用下标访问里面的东西,还可以在尾部添加或删除东西,在指定位置插入或删除东西,还能和另一个箱子的东西互换。

1.3 迭代器失效问题

vector的操作中,有些操作可能会导致迭代器失效,如resizereserveinsertassignpush_back等。当这些操作引起底层数组重新分配时,原来的迭代器会指向无效的内存区域,继续使用这些迭代器会导致程序崩溃。因此,在这些操作后,必须重新获取迭代器。

#include <vector>
#include <iostream>int main() {std::vector<int> v{1, 2, 3, 4, 5, 6};auto it = v.begin();// 操作可能导致迭代器失效v.assign(100, 8);// 必须重新获取迭代器it = v.begin();while (it != v.end()) {std::cout << *it << " ";++it;}std::cout << std::endl;return 0;
}

小李的理解迭代器就像是箱子里的一个标记,告诉我们从哪里开始访问东西。但是如果箱子重新调整过,原来的标记就会失效,所以每次调整后需要重新放置标记。

2. vector深度剖析及模拟实现

 

2.1 std::vector的模拟实现

要深入理解vector,可以尝试实现一个简单的模拟版本。下面是一个简化的vector实现示例:

#include <iostream>  // Include this header for std::cout and std::endltemplate <typename T>
class Vector {
private:T* data;size_t sz;size_t cap;void reallocate(size_t new_cap) {T* new_data = new T[new_cap];for (size_t i = 0; i < sz; ++i) {new_data[i] = std::move(data[i]);}delete[] data;data = new_data;cap = new_cap;}public:Vector() : data(nullptr), sz(0), cap(0) {}void push_back(const T& value) {if (sz == cap) {reallocate(cap == 0 ? 1 : cap * 2);}data[sz++] = value;}size_t size() const { return sz; }size_t capacity() const { return cap; }T& operator[](size_t index) { return data[index]; }~Vector() { delete[] data; }
};int main() {Vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);std::cout << "v size: " << v.size() << ", capacity: " << v.capacity() << std::endl; // v size: 3, capacity: 4for (size_t i = 0; i < v.size(); ++i) {std::cout << v[i] << " ";}std::cout << std::endl; // 1 2 3return 0;
}

解释

  • v.push_back(1), v.push_back(2), v.push_back(3)将三个元素插入vector
  • 初始容量为0,每次容量不足时容量翻倍,最终容量为4,大小为3。
  • 输出vector大小和容量,结果为v size: 3, capacity: 4
  • 输出所有元素,结果为1 2 3

 

小李的理解这个模拟实现就像是自己动手做一个可扩展的箱子,当箱子满了,我们自己找个更大的地方搬过去,这样就可以不断地增加箱子里的东西。

2.2 memcpy的使用问题

vector的实现中,有时会使用memcpy来复制内存。但是,如果元素类型是自定义类型,且涉及资源管理,memcpy的浅拷贝会导致问题,如内存泄漏或程序崩溃。因此,对于自定义类型,应避免使用memcpy,而应使用元素的拷贝构造函数。

#include <iostream>
#include <cstring>class MyClass {
public:int* data;MyClass(int value) {data = new int(value);}~MyClass() {delete data;}
};int main() {MyClass obj1(10);MyClass obj2(20);// 浅拷贝导致问题std::memcpy(&obj2, &obj1, sizeof(MyClass));std::cout << *obj2.data << std::endl; // 可能导致程序崩溃return 0;
}

 

小李的理解memcpy就像是复制一个箱子里的东西,但如果箱子里的东西需要特别处理(比如需要手动管理的资源),直接复制可能会出问题,应该用正确的方法来复制这些东西。、

 

 结论:如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。

2.3 动态二维数组

使用vector可以方便地构建动态二维数组,例如,生成杨辉三角:

#include <vector>
#include <iostream>void generate_pascals_triangle(size_t n) {std::vector<std::vector<int>> vv(n);for (size_t i = 0; i < n; ++i) {vv[i].resize(i + 1, 1);for (size_t j = 1; j < i; ++j) {vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];}}for (const auto& row : vv) {for (int val : row) {std::cout << val << " ";}std::cout << std::endl;}
}int main() {size_t n = 5; // You can change this value to generate more rows of Pascal's Trianglegenerate_pascals_triangle(n);return 0;
}

小李的理解动态二维数组就像是很多小箱子放在一个大箱子里,每个小箱子可以根据需要调整大小,用来存放不同数量的东西。比如杨辉三角,每一行的小箱子里放的东西都不一样多。

总结

C++中的vector是一个动态数组,可以根据需要自动调整大小。它使用连续的内存空间,像普通数组一样可以通过下标快速访问元素,但与普通数组不同的是,vector可以动态增加或减少元素。创建vector有多种方式,包括默认构造、指定大小和默认值、拷贝构造等。常用的操作有插入、删除、访问和遍历元素,vector还提供了容量管理的方法,如reserveresize,以优化性能。在使用过程中需要注意迭代器失效的问题,当vector重新分配内存时,原来的迭代器会失效,必须重新获取。通过这些特性,vector在处理动态数据时非常方便和高效。

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

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

相关文章

js实现移动蒙版层

移动蒙版层 可在整个页面拖动方块&#xff0c;但方块不能超出页面 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">…

阿里巴巴开源自然语音交互框架;在抱抱脸上使用LivePortrait;58种提示技术的工具库

✨ 1: FunAudioLLM FunAudioLLM是一个为人类和大型语言模型&#xff08;LLMs&#xff09;之间自然语音交互打造的语音理解和生成基础框架。 FunAudioLLM 是阿里巴巴集团Tongyi SpeechTeam推出的用于增强人类与大语言模型&#xff08;LLM&#xff09;自然语音交互的框架。该框…

去中心化 RAG 先行者,KIP Protocol 如何保护数据所有权、激活 AI 资产

AI 时代&#xff0c;人人都应实现 KnowledgeFi 的梦想或许并不遥远&#xff0c;KIP Protocol 正在生动践行这一价值理念&#xff0c;带动去中心化数字产权的创建与盈利&#xff0c;面向 CryptoAI 的蓝海市场迈出创新探索的技术步伐&#xff0c;朝着 Web3 行业打造去中心化 AI 的…

前端面试题25(css常用的预处理器)

在前端开发领域&#xff0c;CSS预处理器在面试中经常被提及&#xff0c;其中最流行的三种预处理器是Sass、LESS和Stylus。下面分别介绍它们的特点和优势&#xff1a; 1. Sass&#xff08;Syntactically Awesome Style Sheets&#xff09; 优势&#xff1a; 变量&#xff1a;允…

RK3588开发笔记(四):基于定制的RK3588一体主板升级镜像

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140288662 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

【机器学习】使用决策树分类器预测汽车安全性的研究与分析

文章目录 一、决策树算法简介决策树的结构分类和回归树 (CART)决策树算法术语决策树算法直觉 二、属性选择度量信息增益熵 基尼指数计算分割基尼指数的步骤 三、决策树算法中的过度拟合避免过度拟合的方法 四、导入库和数据可视化探索性数据分析重命名列名查看数据集的总结信息…

python库(8):re库实现字符串处理

1 re库简介 Python 的re库是一个功能强大的正则表达式模块&#xff0c;它允许用户执行各种复杂的字符串匹配和处理任务。 以下是re库的主要功能&#xff1a; 搜索&#xff1a;re.search() 用于搜索字符串中第一次出现的模式。匹配&#xff1a;re.match() 从字符串的开始位置…

ggplot2绘图点的形状不够用怎么办?

群里有这么一个问题&#xff1a; 请问老师&#xff0c;fviz_pca_ind 做pca&#xff0c;当设置geom.ind “point”&#xff0c;group>6时&#xff0c;就不能显示第7&#xff0c;8组的点&#xff0c;应该如何处理&#xff08;在不设置为文本的情况下&#xff09;&#xff0c;…

该让医疗垂类大模型,走出“试题”了

图源&#xff1a;123rf “现阶段&#xff0c;许多医疗垂类大模型就是伪命题&#xff0c;推理能力不行。” 一位观望大模型已久的医疗从业者玄彬&#xff08;化名&#xff09;发出了极为尖锐的批评。 在他看来&#xff0c;目前人工智能是靠scaling law涌现的&#xff0c;模型性…

【前端】使用chrom浏览器Network,查看前后台数据传输请求

使用chrom浏览器Network查看前后台数据传输请求 写在最前面查看前后台数据传输请求① 首先&#xff0c;打开开发者工具&#xff08;F12&#xff09;打开控制台&#xff0c;切换到Network面板。Network面板右键界面copy ②清空请求log ctrle两次或者点击clear图标 案例展示&…

鸿蒙开发HarmonyOS NEXT (三) 熟悉ArkTs (上)

一、自定义组件 1、自定义组件 自定义组件&#xff0c;最基础的结构如下&#xff1a; Component struct Header {build() {} } 提取头部标题部分的代码&#xff0c;写成自定义组件。 1、新建ArkTs文件&#xff0c;把Header内容写好。 2、在需要用到的地方&#xff0c;导入…

html——VSCode的使用

快捷键 快速生成标签&#xff1a;标签名tab 保存文件&#xff1a;CtrlS 设置自动保存【文件】→【自动保存】 快速查看网页效果&#xff1a;右击→Open in Default Browser 快捷键&#xff1a;altb 注意&#xff1a;必须安装了open in brows…

windows10下的游戏怎么卸载?

在Windows 10中卸载游戏可以通过多种途径进行&#xff0c;下面是一些常见的方法&#xff1a; 方法一&#xff1a;通过“设置”应用卸载 1. 点击左下角的“开始”按钮&#xff0c;打开“开始”菜单。 2. 选择“设置”图标&#xff08;齿轮形状&#xff09;。 3. 在“设置”窗…

2024年5款最佳免费博客程序——对比和测评

多年来&#xff0c;我试用了许多不同的博客网站&#xff0c;并评估了它们在各种需求上的表现。这篇文章记录了我的发现&#xff08;截至2024年&#xff09;&#xff0c;旨在帮助您为您的项目选择最佳解决方案。 我将介绍五个非常优秀的博客平台&#xff0c;它们让您能够轻松创建…

知识改变命运 第二集:Java的数据类型与变量

数据类型与变量 1. 字面常量2. 数据类型3. 变量3.1 变量概念3.2 语法格式3.3 整型变量3.3.1 整型变量3.3.2 长整型变量3.3.3 短整型变量3.3.4 字节型变量 3.4 浮点型变量3.4.1 双精度浮点型3.4.2 单精度浮点型 3.5 字符型变量3.6 布尔型变量3.7 类型转换3.7.1 自动类型转换(隐式…

mybatilsplaus 常用注解

官网地址 baomidou注解配置

CATIA二次开发VBA入门(5)——catia文档操作vb.net程序案例,打开catia文件,进行视图操作,退出程序

目录 引出catia文档操作案例1.初始化窗体&#xff0c;始终置顶始终置顶方式2 2.打开文件3.视图切换4.退出5.完整代码 总结认识CATIA二次开发刘瑞欣 vb程序设计教程Excel中的vba开发catia中的vba开发 宏的录制、回放和编辑宏代码精简画圆柱阵列宏Macro文件的3种类型宏的保存&…

如何安全隐藏IP地址,防止网络攻击?

当您想在互联网上保持隐私或匿名时&#xff0c;您应该做的第一件事就是隐藏您的 IP 地址。您的 IP 地址很容易被追踪到您&#xff0c;并被用来了解您的位置。下面的文章将教您如何隐藏自己&#xff0c;不让任何试图跟踪您的活动的人发现。 什么是 IP 地址&#xff1f; 首先&am…

Apache Flink核心特性应用场景

Flink的定义 Apache Flink是一个分布式处理引擎&#xff0c;用于处理 无边界数据流&#xff0c; 有边界数据流上金秀贤有状态的计算。Flink能在所有常见的集群环境中运行&#xff0c;并能以内存速度和任意规模进行计算如下Flink官网的一张图 Flink 与Spark的区别 Flink 中处…

白盒测试的概念、特点、应用阶段、实施流程、现状与前景

文章目录 前言一、白盒测试的应用阶段二、白盒测试的特点三、白盒测试的流程四、白盒测试的现状与前景总结 前言 白盒测试&#xff08;White Box Testing&#xff09;&#xff0c;又称为结构测试&#xff08;Structural Testing&#xff09;、透明盒测试&#xff08;Glass Box…