C++:STL(四)之vector的基本介绍与使用方式|容器接口

news2024/11/18 5:34:16


Blog’s 主页: 白乐天_ξ( ✿>◡❛)
🌈 个人Motto:他强任他强,清风拂山冈!
🔥 所属专栏:C++深入学习笔记
💫 欢迎来到我的学习笔记!

一、C/C++中的字符串

1.1. C语言中的数组

在C语言中,数组是一组相同类型元素的有序集合。与字符串类型,它的大小在编译时就已经确定无法更改了。

// 大小为5的整形数组
int arr1[5] = { 1,2,3,4,5 };
// 大小为5的浮点型数组
double arr2[5] = { 3.14 };

1.2. C++中的数组

string类似,C++为了更加方便就引入了一个支持可动态数组大小数组的序列容器vectorvector的特点如下:

  1. 基本特性
    • vector是可变大小的序列容器,像数组一样使用连续存储空间存储元素,能通过下标高效访问元素。
    • 与数组的区别在于vector的大小可动态改变,并且这种大小的改变由容器自动处理。
  2. 存储机制
    • 本质上,vector使用动态分配数组存储元素。
    • 当插入新元素时,可能需要重新分配数组大小,这一过程代价相对较高,但vector不会每次插入都重新分配。
  3. 空间分配策略
    • vector会分配额外空间以应对可能的增长,不同库在空间使用和重新分配上有不同策略。
    • 重新分配的间隔大小应是对数增长,使得在末尾插入元素的操作能在常数时间复杂度内完成。
    • 为了获得管理存储空间和有效动态增长的能力,vector会占用更多存储空间。
  4. 与其他容器比较
    • 相较于dequelistforward_list等动态序列容器,vector访问元素更高效。
    • 在末尾添加和删除元素相对高效。
    • 对于不在末尾的删除和插入操作,vector的效率更低。
    • vector具有比listforward_list更好的统一迭代器和引用。
  5. 还要注意:
    • 每次使用vector都需要包含头文件<vector>vector是一个模板类,所以在使用时需要显示实例化。
//整型数组
vector<int> v1;
//浮点型数组
vector<double> v2;

二、vector的接口

vector的接口和string的接口很相似。

2.1 vector类对象的默认成员函数

构造函数声明接口说明
vector()构造函数
vector(size_type n,const value_type& val = value_type())构造并初始化n个val
vector(Inputlterator first, Inputilerator last)使用迭代器进行初始化构造
vector(const vector& x)拷贝构造
vector& operator=(const vector& x)赋值重载

2.1.1 构造函数

explicit vector(const allocator_type& alloc = allocator_type());
explicit vector(size_type n, const value_type& val = value_type(),
                const allocator_type& alloc = allocator_type());
template <class InputIterator>
         vector(InputIterator first, InputIterator last,
                const allocator_type& alloc = allocator_type());
vector(const vector& x);
// 其中的 allocator 是空间配置器,只是用于分配空间,目的为增加申请释放空间的效率
// 对于vector来说是一个模板类,所以我们在实例化时要声明其内置类型。
  1. 无参构造函数
    • 示例代码:
// 构造函数创建一个空的vector,初始大小为0
vector<int> v1;// 无参(没有传入任何的参数)
  • 这种构造函数创建一个空的vector,其初始化大小为0。
  1. 指定个数和初始值构造函数
    • 示例代码:
vector<int> v2(3, 2);
for (int i = 0; i < v2.size(); i++)
{
    cout << v2[i] << " ";
}
// 2 2 2 
  • 这里构造了一个包含3个元素每个元素初始值为2的vector
  1. 使用迭代器区间构造函数

    • 示例代码
string s("abcd");
vector<int> v3(s.begin(), s.end());
  • 这里使用迭代器区间来构造vector。需要注意的是,如果vector的内置类型与迭代器所指向的元素类型不匹配(如上述代码中v3vector<int>,而sstring类型,会发生隐式类型转换,将字符的 ASCII 值存储到vector中)。如果要正确存储字符,应声明为vector<char>
  • 此外,还可以利用指针(其底层基于迭代器思维)来初始化vector,例如:
// 利用天然的迭代器 —— 指针进行初始化
int a[] = {1, 2, 3, 4};
vector<int> v2(a, a + 4);
  • vector是模板类,在实例化时需要声明其内置类型,如template <class T, class Alloc = allocator<T>> class vector;

2.1.2 拷贝构造函数

  • 示例代码:
vector<int> v1(3, 2);
vector<int> v2(v1);// 拷贝构造
// 2  2  2 
  • 拷贝构造函数用于创建一个与已有vector完全相同的新vector

2.1.3 赋值重载

  • 示例代码:
vector<int> v1(3, 2);// 创建3个2
vector<int> v2;
v2 = v1;// 赋值重载
//v3 = v1;//error:要求是同一类型的vector类
// 2 2 2
  • 赋值重载操作将一个vector的值复制到另一个已存在的vector中。(值之间的拷贝)
  • 要求两个vector的类型相同,不同类型的vector之间无法进行赋值操作。

2.2 vector类对象的访问及遍历操作

2.2.1 opeartor[] 和 at()

  1. operator[]
    • 示例代码
#include <vector>
#include <iostream>

int main()
{
	std::vector<int> v({ 1,2,3,4,5 });
	for (size_t i = 0; i < v.size(); i++)
	{
		std::cout << v[i] << " ";
	}
	std::cout << std::endl;
	return 0;
}
// 1 2 3 4 5
  • operator[]是一种常见的访问vector元素的方式,通过下标来获取元素。
  • 下标越界会导致断言错误。
  1. at()
    • 示例代码
#include <vector>
#include <iostream>

int main()
{
	std::vector<int> v{ 1,2,3,4,5 };
	for (size_t i = 0; i < v.size(); i++)
	{
		std::cout << v.at(i) << " ";
	}
	std::cout << std::endl;
	return 0;
}
  • at()函数也用于访问元素,与operator[下标]不同的是,如果下标越界,at(下标)会抛出异常。

2.2.2 迭代器

  1. begin() 和 end()(正向迭代器)
    • 示例代码
#include <vector>
#include <iostream>

int main()
{
	std::vector<int> v{ 1,2,3,4,5 };
	std::vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		std::cout << *it << " "; 
		it++;
	}
	std::cout << std::endl;
	return 0;
}
// 1 2 3 4 5
  • begin()返回指向vector第一个元素的迭代器,end()返回指向vector最后一个元素位置之后的迭代器。通过迭代器遍历vector中的元素。
  1. rbegin() 和 rend()(反向迭代器)
    • 示例代码
#include <vector>
#include <iostream>
int main()
{
	std::vector<int> v{ 1,2,3,4,5 };
	std::vector<int>::reverse_iterator rit = v.rbegin();
	while (rit != v.rend())
	{
		std::cout << *rit << " ";
		rit++;
	}
	std::cout << std::endl;
	return 0;
}
// 5 4 3 2 1
  • rbegin()返回指向vector最后一个元素的反向迭代器,rend()返回指向vector第一个元素之前的反向迭代器,用于反向遍历vector

2.2.3 范围for

- 示例代码
#include <vector>
#include <iostream>

int main()
{
	std::vector<int> v{ 1,2,3,4,5 };
	for (auto e : v)
	{
		std::cout << e << " ";
	}
	std::cout << std::endl;
	return 0;
}
// 1  2  3  4  5
  • 范围for是一种简洁的遍历vector元素的方式吗,基于迭代器实现。

2.2.4 sort

vector<int> v({1,2,3,4,5});
sort(v.begin(), v.end());
for(auto e : v)
{
    cout<< e << " ";
}
cout << endl;

迭代器与sort概述

  1. 迭代器与algorithm 头文件中的sort函数
    • 在涉及迭代器时,algorithm头文件中的sort函数是一个重要的库函数。它有两个重载形式。
  2. sort函数的第一个重载形式:升序排列。
// 传入的仿函数对象为less<int>(),升序排列
sort(v.begin(), v.end());
  1. sort函数的第二个重载形式:默认升序排列
    • 最后一个参数是 STL 六大组件中的仿函数。
    • 默认传入的仿函数对象为less<int>(),这是一个小堆,代表升序。
    • 如果要进行降序排序,需要传入的仿函数对象为greater<int>()(这是一个大堆,代表降序),如sort(v.begin(), v.end(), greater<int>());,运行结果可观察到确实进行了降序排序。
// 传入的仿函数对象为greater<int>() ,降序排列
sort(v.begin(), v.end(), greater<int>());

反向迭代器与sort函数:

  1. 反向迭代器传递的可行性
    • 除了正向迭代器,反向迭代器也可以传递给sort函数。
sort(v.rbegin(), v.rend());
  1. 反向迭代器排序的特性
    • 当传递反向迭代器并默认按照升序排序时,从后往前进行升序排序就相当于从前往后进行降序排序。

总结:

  1. 升序排序(正向迭代器 - 第一个重载形式)
sort(v.begin(), v.end());
- 效果:按照默认的升序规则对`v`容器中的元素进行排序。
  1. 降序排序(正向迭代器 - 第二个重载形式)
sort(v.begin(), v.end(), greater<int>());
- 效果:通过传入`greater<int>()`仿函数对象,实现对`v`容器中的元素进行降序排序。
  1. 反向迭代器排序
sort(v.rbegin(),v.rend());
- 效果:从后往前进行升序排序,等同于从前往后进行降序排序。

2.3 vector类对象的常见容量操作

2.3.1 size 和 capacity

  1. size
    • 示例代码
#include <vector>
#include <iostream>
int main()
{
	std::vector<int> v(10, 1);
	std::cout << "v的大小:" << v.size() << std::endl;
	return 0;
}
// v的大小:10
  • size函数返回vector中当前元素的个数。
  1. capacity
    • 示例代码:
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> v;
    std::cout << "初始容量: " << v.capacity() << std::endl;
    for (int i = 0; i < 10; i++)
    {
        v.push_back(i);
    }
    std::cout << "添加10个元素后的容量: " << v.capacity() << std::endl;
    return 0;
}
// 初始容量: 0
// 添加10个元素后的容量: 13
  • capacity函数返回vector当前分配的存储空间能够容纳的元素数量。在不同的 STL 版本中,vector的扩容机制有所不同。
  • 例如在VS下的扩容机制是呈现 1.5 进行增长的,其STL是P.J.版本;在 Linux 下却始终是呈现的一个2倍的扩容机制,其STL是SGI版本

2.3.2 empty

- 示例代码:
#include <vector>
#include <iostream>

int main() 
{
    std::vector<int> v;
    std::cout << "初始时是否为空: " << (v.empty() ? "是" : "否") << std::endl;
    v.push_back(1);
    std::cout << "添加元素后是否为空: " << (v.empty() ? "是" : "否") << std::endl;
    return 0;
}
// 初始时是否为空: 是
// 添加元素后是否为空: 否
  • empty函数用于判断vector是否为空,如果vector中没有元素,则返回true,否则返回false

2.3.3 reserve 和 resize

  1. reserve

    • 示例代码:
#include <vector>
#include <iostream>

int main() 
{
    std::vector<int> v;
    v.reserve(10);
    std::cout << "预留空间后的容量: " << v.capacity() << std::endl;
    return 0;
}
// 预留空间后的容量: 10
  • 示例代码2:
void TestVectorExpandOP()
{
	vector<int> v;
	size_t sz = v.capacity();
	v.reserve(100); // 提前将容量设置好,可以避免一遍插入一遍扩容
	cout << "making bar grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}
  • reserve函数用于预先分配指定大小的存储空间,以避免在后续添加元素时频繁扩容。需要注意的是,reserve只改变vector的容量,不改变元素个数(size)。
  1. resize
    • 示例代码:
#include <vector>
#include <iostream>

int main() 
{
    std::vector<int> v;
    v.resize(3);
    std::cout << "调整大小后的元素个数: " << v.size() << std::endl;
    for (int i = 0; i < v.size(); i++) 
    {
        std::cout << v[i] << " ";
    }
    std::cout << std::endl;
    return 0;
}
// 调整大小后的元素个数: 3
// 0 0 0
  • resize函数用于改变vector的元素个数。
    • 如果新的大小大于当前大小,则会在末尾添加默认值(对于基本类型,默认值为 0)的元素;
    • 如果新的大小小于当前大小,则会删除多余的元素。

2.4 vector类对象的修改操作

函数名称功能
push_back在数组后追加元素
insert在指定位置追加元素
assign使用指定数组替换原数组
pop_back删除数组最后一个元素
erase删除数组指定部分区间
swap交换两个数组

2.4.1 push_back 和 pop_back

  1. push_back
    • 示例代码:
#include <vector>
#include <iostream>
#include <string>

int main() 
{
    std::vector<std::string> v;
    std::string name1("张三");
    v.push_back(name1);
    v.push_back(std::string("李四"));
    v.push_back("王五");
    for (auto str : v) 
    {
        std::cout << str << " ";
    }
    std::cout << std::endl;
    return 0;
}
// 张三 李四 王五
  • push_back函数用于在vector的末尾添加一个元素。它支持多种方式添加元素,如添加已有的对象、匿名对象或者利用单参数构造函数引发的隐式类型转换来添加元素。
  1. pop_back
    • 示例代码:
#include <vector>
#include <iostream>

int main() 
{
    std::vector<int> v = { 1, 2, 3 };
    v.pop_back();
    for (auto num : v) 
    {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}
// 1 2 (没有3)
  • pop_back函数用于删除vector末尾的一个元素。

2.4.2 insert 和 erase

  1. insert
    • 示例代码:
#include <vector>
#include <iostream>

int main() 
{
    std::vector<std::string> v;
    v.insert(v.begin(), "刘琦");
    v.push_back("赵六");
    for (auto str : v) 
    {
        std::cout << str << " ";
    }
    std::cout << std::endl;
    return 0;
}
// 刘琦 赵六
  • insert函数用于在指定位置之前插入一个元素。它有多种重载形式,可以根据不同的需求在vector中的不同位置插入元素。
  1. erase
    • 示例代码:
#include <vector>
#include <iostream>

int main() 
{
    std::vector<int> v = { 1, 2, 3, 4 };
    v.erase(v.begin() + 1);
    for (auto num : v) 
    {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}
// 1 3 5
  • erase函数用于删除指定位置的元素。它有两种重载形式,一种是传递单个迭代器来删除指定位置的元素,另一种是传递迭代器区间来删除多个元素。在想要删除容器内指定数据时,如果仅依靠vector本身的接口是无法直接做到的,需要借助<algorithm>头文件中的find函数。

2.4.3 find函数与迭代器失效

  • vector中,查找指定元素通常使用<algotithm>头文件中的find函数。
  • 示例代码:
#include <vector>
#include <iostream>
#include <algorithm>

int main() 
{
    std::vector<int> v = { 1, 2, 3, 1, 4 };
    auto pos = std::find(v.begin(), v.end(), 1);
    while (pos != v.end()) 
    {
        v.erase(pos);
        pos = std::find(pos, v.end(), 1);
    }
    for (auto num : v) 
    {
        std::cout << num << " ";
    }
    return 0;
}
  • 在这个例子中,当我们使用erase函数删除元素后,vector的内部结构可能会发生改变,导致之前获取的迭代器pos失效。如果继续使用失效的迭代器,会导致未定义行为,如程序崩溃或者产生错误的结果。为了避免这种情况,在删除元素后,需要重新获取有效的迭代器,如代码中在每次删除元素后重新调用find函数获取下一个匹配元素的迭代器。

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

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

相关文章

【机器学习】——决策树以及随机森林

文章目录 1. 决策树的基本概念与结构1.1 决策树的构建过程 2. 决策树的划分标准2.1 信息增益&#xff08;Information Gain&#xff09;2.2 信息增益比&#xff08;Information Gain Ratio&#xff09;2.3 基尼指数&#xff08;Gini Index&#xff09;2.4 均方误差&#xff08;…

极限基本类型小结

极限基本类型小结 在之前的文章中已经看过了极限的多种基本类型&#xff0c;下面展示一些各种基本类型的代表性的图像&#xff0c;通过观察下面的图像可以帮助我们回顾函数在趋近于某一点时函数值的行为&#xff08;这也叫极限值&#xff09;&#xff0c;也生动的描述了各种极…

老古董Lisp实用主义入门教程(12):白日梦先生的白日梦

白日梦先生的白日梦 白日梦先生已经跟着大家一起学Lisp长达两个月零五天&#xff01; 001 粗鲁先生Lisp再出发002 懒惰先生的Lisp开发流程003 颠倒先生的数学表达式004 完美先生的完美Lisp005 好奇先生用Lisp来探索Lisp006 好奇先生在Lisp的花园里挖呀挖呀挖007 挑剔先生给出…

关于工作虚拟组的一些思考

这是学习笔记的第 2493篇文章 因为各种工作协作&#xff0c;势必要打破组织边界&#xff0c;可能会存在各种形态的虚拟组。 近期沉淀了一些虚拟组的管理方式&#xff0c;在一定时间范围内也有了一些起色&#xff0c;所以在不断沉淀的过程中&#xff0c;也在不断思考。 这三个虚…

企业级版本管理工具(1)----Git

目录 1.Git是什么 2.Git的安装和使用 在Ubuntu下安装命令如下&#xff1a; 使用git --version查看已安装git的版本&#xff1a; 使用git init初始化仓库&#xff1a; 使用tree .git列出目录&#xff1a; 使用git config命令设置姓名和邮箱&#xff1a; 加入--global选项…

WPF MVVM入门系列教程(一、MVVM模式介绍)

前言 还记得早些年刚工作的那会&#xff0c;公司的产品从Delphi转成了WPF&#xff08;再早些年是mfc&#xff09;。当时大家也是处于一个对WPF探索的阶段&#xff0c;所以有很多概念都不是非常清楚。 但是大家都想堆技术&#xff0c;就提出使用MVVM&#xff0c;我那会是第一次…

想做个WPS的自动化代码,参考如下:

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

水仙花数求解-C语言

1.问题&#xff1a; 输出100-1000之间所有的“水仙花数”。 2.解答&#xff1a; “水仙花数”是指一个3位数&#xff0c;其各位数字立方和等于该数本身&#xff0c;逐个位数判断即可&#xff0c;写代码的时候要考虑到每一位。 3.代码&#xff1a; #include<stdio.h>//…

9.28QT基础

widget.cpp widegt.h .pro main.cpp 一个仿QQ登录界面 #include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {this->setFixedSize(350,500);this->setStyleSheet("background-color:#e5f0ff;");QLineEdit *edit1 new QLine…

1.2.3 HuggingFists安装说明-MacOS安装

MacOS版安装说明 下载地址 【GitHub】GitHub - Datayoo/HuggingFists4Mac 【百度网盘】https://pan.baidu.com/s/12WxZ-2GgMtbQeP7AcmsyHg?pwd2024 安装说明 环境要求 操作系统&#xff1a;MacOS 硬件环境&#xff1a;至少4核8G 安装步骤 1. 安装Docker环境。若已安装Do…

kali linux 终端复用和界面移动

kali linux 终端复用和界面移动 经验和操作 渗透测试的工具选择考虑 01 能用命令行就不用图形ui 图形ui 容易对细节隐藏&#xff0c;命令行工具的在终端输出的日志相对透明&#xff0c;容易观察和调整 wireshark – tcpdump burpsuit – curl( web 访问相关), wfuzz(模糊测…

带你0到1之QT编程:十七、Http协议实战,实现一个简单服务器和一个客户端进行http协议通信

此为QT编程的第十七谈&#xff01;关注我&#xff0c;带你快速学习QT编程的学习路线&#xff01; 每一篇的技术点都是很很重要&#xff01;很重要&#xff01;很重要&#xff01;但不冗余&#xff01; 我们通常采取总-分-总和生活化的讲解方式来阐述一个知识点&#xff01; …

Python15行代码 tkinter模块 写计算机

之前去备战csp-j了 也有一段时间没更新了 结果白名单没捞着 还差点被我妈打喜 今天闲来无事 写个计算器玩玩 _____________________________________________________________________________ 老规矩 先放代码 from tkinter import *;from math import sqrt;aTk();a.geome…

机械键盘驱动调光DIY--【DAREU】

1 下载键盘对应的驱动&#xff0c;不要装到C盘 达尔优驱动下载中心 2 驱动更改教程 标准模式 键盘功能 鼠标功能 切换灯光 切换配置文件 多媒体 windows快捷键 禁用 Fn 启动程序 文本功能 光标定位 FN模式 灯光效果设置 注意 宏--自定义功能

【SpringCloud】多机部署, 负载均衡-LoadBalance

多机部署, 负载均衡-LoadBalance 1. 负载均衡介绍1.1 问题描述1.2 什么是负载均衡1.3 负载均衡的⼀些实现服务端负载均衡客⼾端负载均衡 2. Spring Cloud LoadBalancer 1. 负载均衡介绍 1.1 问题描述 观察上个章节远程调⽤的代码 根据应⽤名称获取了服务实例列表从列表中选择…

【SQL】未订购的客户

目录 语法 需求 示例 分析 代码 语法 SELECT columns FROM table1 LEFT JOIN table2 ON table1.common_field table2.common_field; LEFT JOIN&#xff08;或称为左外连接&#xff09;是SQL中的一种连接类型&#xff0c;它用于从两个或多个表中基于连接条件返回左表…

CSS布局中的定位

一、position 1.static position: static; 默认值&#xff0c;没有定位2 .relative 相对定位&#xff1a;相对自身原来的位置进行偏移 偏移设置&#xff1a;top、left、right、bottom 相对定位元素的规律&#xff1a; 设置相对定位的盒子会相对于它原来的位置&#xff0c;通…

Nature数据库介绍及个人获取Nature文献下载途径

Springer Nature集团是一家全球领先的从事科研、教育和专业出版的机构&#xff0c;也是世界上最大的学术图书出版公司&#xff0c;同时出版全球最具影响力的期刊&#xff0c;也是开放获取领域的先行者。Springer Nature在2015年由Nature出版集团&#xff08;Nature Portfolio&a…

JS加密=JS混淆?(JS加密、JS混淆,是一回事吗?)

JS加密、JS混淆&#xff0c;是一回事吗&#xff1f; 是的&#xff01;在国内&#xff0c;JS加密&#xff0c;其实就是指JS混淆。 1、当人们提起JS加密时&#xff0c;通常是指对JS代码进行混淆加密处理&#xff0c;而不是指JS加密算法&#xff08;如xor加密算法、md5加密算法、…

正点原子——DS100示波器操作手册

目录 基础按键&#xff1a; 快捷键 主界面&#xff1a; 垂直设置&#xff1a; 通道设置&#xff1a; 探头比列&#xff1a; 垂直档位&#xff1a; 垂直偏移&#xff1a; 幅度单位&#xff1a; 水平设置&#xff1a; 触发方式&#xff1a; 测量和运算: 光标测量&am…