C++初阶:vector的使用与自实现

目录

  • 1. vector与顺序表
  • 2. vector的使用
    • 2.1 默认成员函数
    • 2.2 vector的迭代器
    • 2.3 容量相关成员函数
    • 2.4 遍历访问方式
    • 2.5 vector的修改操作
  • 3. vector的自实现
    • 3.1 自实现功能
    • 3.2 vector的结构与自实现的逻辑
    • 3.3 自实现vector功能接口

1. vector与顺序表

  1. 我们在初阶数据结构中学习了简单数据结构链表,这一数据结构的要求为底层物理空间连续,且支持头插尾插等插入操作,头删尾删等删除操作。在C++的STL中也有对这一简单数据结构的实现,将其模板化加入库中,不仅提高了泛用性还提供了便捷的接口,接下来,就让我们对这一容器vector进行掌握学习。

2. vector的使用

2.1 默认成员函数

1. 构造与拷贝构造

//默认构造,内容为空
vector<int> v1;

//指定数据内容与个数构造
vector<int> v2(3,100);

//迭代器区间构造
vector<int> v3(v2.begin(), v2.end());

//使用列表构造
vector<int> v4({1,2,3,4,5});

//拷贝构造
vector<int> v5(v4);

2. operator=赋值运算符重载

vector<int> v1(3, 100);
vector<int> v2;
//给已经存在的对象赋值
v2 = v1;

//当使用赋值运算符对还没有产生的对象赋值时,会调用拷贝构造
vector<int> v3 = v1;

2.2 vector的迭代器

//正向反向迭代器
vector<int> v1;
vector<int>::iterator it1 = v1.begin();
vector<int>::iterator it2 = v2.end();

vector<int>::reverse_iterator it3 = v3.rbegin();
vector<int>::reverse_iterator it4 = v4.rend();

//const修饰的正反向迭代器
vector<int>::const_iterator it5 = v5.cbegin();
vector<int>::const_iterator it6 = v6.cend();

vector<int>::const_reverse_iterator it7 = v7.crbegin();
vector<int>::const_reverse_iterator it7 = v8.crend();

2.3 容量相关成员函数

1. 容量大小

vector<int> v1(3, 100);

//当前存储的数据个数
cout << v1.size() << endl;

//理论上最多能够存储的数据个数,无参考意义
cout << v1.max_size() << endl;

//当前vector的容量
cout << v1.capacity() << endl;

//检测当前vector是否为空,无存储元素
cout << v1.empty() << endl;

2. 扩容与缩容

vector<int> v2(3,100);

//将当前vector扩容至指定大小,1.5被扩容
v2.reserve(10);

//将当前vector扩容至指定大小,再将扩容出的部分用指定数填满
//vector长度跟随增长,未指定时,默认使用0填充
v2.resize(20);

//缩容,将当前大于有效数据长度的部分缩减
//将缩小当前数据的长度
v2.shrink_to_fit();

2.4 遍历访问方式

vector<int> v1(5,100);

//operator[]运算符重载
//报警告
v1[0];

//抛异常
v1.at[1];

//返回vector的首尾元素
v1.front();
v.back();

2.5 vector的修改操作

1. 插入,赋值操作

vector<int> v1(5, 100);
vector<int> v2;
//assign
//指定用多少个什么数赋值
v2.assgin(4,100);
//使用迭代器区间赋值
v2.assgin(v1.begin(), v2.end());

//尾插
v2.push_back(10);

//随机插入
//在指定位置(迭代器表示),插入一个数
v2.insert(v2.begin(), 10);

//在指定迭代器位置,插入n个数
v2.insert(v2.begin(), 3, 10);

//在指定迭代器位置插入一段迭代器区间
v2.insert(v2.begin(), v1.begin(), v1.end());
  1. 补充:头插的效率很低,且可以通过随机插入代为实现头插,所以vector没有去实现头插

2. 删除操作

vector<int> v1(10, 100);

//尾删
v1.pop_back();

//删除指定迭代器位置的数据
v1.erase(v1.begin());

//删除指定迭代器区间的数据
v1.erase(v1.begin(), v1.begin() + 3);

//清空vector
v1.clear();

//补充:成员函数swap
vector<int> v2(10, 9);

v1.swap(v2);
  1. 补充:STL为开源项目,查看源代码的方式
    <1> 不要进行一行一行的细节查看
    <2> 查看整体框架
    <3> 成员变量,构造函数,插入操作作为突破口

3. vector的自实现

3.1 自实现功能

  1. 默认成员函数:构造,拷贝构造,析构,operator=赋值运算符重载
  2. 遍历访问方式:iterator,operator[],front,back
  3. 容量相关成员函数:capacity,size,reserve,resize
  4. 插入删除操作:push_back,pop_back,insert,erase
  5. 补充:swap

3.2 vector的结构与自实现的逻辑

  1. 不再单独创建成员变量来记录容量与数据长度,采用三个迭代器的方式来进行信息的维护

在这里插入图片描述

  1. 我们可以通过合适的实现逻辑与复用完成对vector接口的实现

在这里插入图片描述

3.3 自实现vector功能接口

1. 成员变量与迭代器

template<class T>
class vector
{
public:
	typedef T* iterator;
	typedef const T* const_iterator;

	//迭代器
	iterator begin()
	{
		return _start;
	}

	iterator begin() const
	{
		return _start;
	}

	iterator end()
	{
		return _finish;
	}

	iterator end() const
	{
		return _finish;
	}

private:
	iterator _start = nullptr;
	iterator _finish = nullptr;
	iterator _endofstorage = nullptr;	
};

2. 容量相关

size_t size()
{
	return _finish - _start;
}

size_t capacity()
{
	return _endofstorage - _start;
}

3. 扩容与插入删除

  1. reserve
void reserve(size_t n)
{
	if (n > capacity())
	{
		//开辟空间
		T* tmp = new T[n];
		int old = _finish - _start;

		//检测是否需要赋值
		if (_start)
		{
			for (int i = 0; i < old; i++)
			{
				tmp[i] = _start[i];
			}

			//释放旧空间
			delete[] _start;
		}
		
		//直接调用capacity与size()会导致报错
		//扩容后指针失效
		_start = tmp;
		_finish = _start + old;
		_endofstorage = _start + n;
	}
}

在这里插入图片描述

  1. push_back
void push_back(const T& val)
{
	if (_finish == _endofstorage)
	{
		int newcapacity = capacity() == 0 ? 4 : 2 * capacity();
		reserve(newcapacity);
	}

	*_finish = val;
	_finish++;
}
  1. resize
//C++中为内置类型也添加了构造
//double 0.0,char '\0',int 0
void resize(size_t n, T val = T())
{
	if(n > size())
	{
		//扩容
		reserve(n);
		//填值
		while(_finish < _start + n)
		{
			*_finish = val;
			++_finish;
		}
	}
	else
	{
		//缩容
		_finish = _start + n;
	}
}
  1. pop_back
void pop_back()
{
	assert(size() > 0);

	--_finish;
}

4. 默认成员函数

  1. 无参构造与拷贝构造
//构造
vector()
{}

//拷贝构造
vector(const vector<T>& v)
{
	auto it = v.begin();
	//范围for
	while (it < v.end())
	{
		push_back(*it);
		it++;
	}
}
  1. 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> v)
{
	swap(v);

	return *this;
}
  1. 迭代器区间构造
    <1> 迭代器区间构造,支持使用其他容器初始化
    <2> string,list,数组(数组的原生指针是天然的迭代器)
template<class Inputiterator>
vector(Inputiterator it1, Inputiterator it2)
{
	//其他数据结构的内存空间可能不连续
	while (it1 != it2)
	{
		push_back(*it1);
		it1++;
	}
}
  1. 用指定的n个数进行构造
    <1> 当参数为两个int时,迭代器区间构造的调用优先级会比参数类型为size_t, T的构造函数高
    <2> 重载如下第二个构造函数就可以避免上述情况的发生
//n个指定值构造
vector(size_t n, T val = T())
{
	//直接复用resize
	resize(n, val);
}

//两个参数都为int时,保证调用此构造
vector(int n, T val = T())
{
	resize(n, val);
}
  1. 析构函数
~vector()
{
	if (_start)
	{
		delete[] _start;
	}

	_start = _finish = _endofstorage = nullptr;
}

5. 迭代器失效,深浅拷贝与erase,insert

  1. insert(向指定迭代器位置插入一个元素)
//在pos位置插入一个数
iterator insert(iterator pos, T val)
{
	assert(pos >= _start && pos <= _finish);

	int old = pos - _start;
	//扩容后迭代器失效
	if (_finish == _endofstorage)
	{
		int newcapacity = capacity() == 0 ? 4 : 2 * capacity();
		reserve(newcapacity);
	}

	//避免自定义类型浅拷贝,不能使用memmove等函数
	//向后依次挪移,使用存储数据类型的赋值重载
	//迭代器插入没有下标插入size_t类型的特殊处理
	pos = _start + old;
	auto end = _finish - 1;
	while (end >= pos)
	{
		*(end + 1) = *end ;
		end--;
	}

	*pos = val;
	_finish++;

	//防止迭代器失效,可以使用插入与++分离的方式
	//不能保证不扩容
	return pos;
}

扩容导致的迭代器失效:
在这里插入图片描述

  1. 补充1:使用memove只会进行浅拷贝,当存储数据是自定义类型时,进行赋值时,不会去析构旧空间,会导致空间泄漏。
  1. erase(删除指定迭代器位置元素)
iterator erase(iterator pos)
{
	assert(pos >= _start && pos < _finish);

	//迭代器失效,memmove浅拷贝
	auto it = pos + 1;
	while (it < _finish)
	{
		*(it - 1) = *it;
		it++;
	}

	_finish--;

	return pos;
}
  1. 补充2:迭代器失效
    <1> 不同平台对insert与erase的实现方式不同,可能会进行空间的扩容或缩容,这样就会导致原本迭代器的失效。
    <2> 删除或插入后,原本迭代器指向元素的相对位置改变,插入删除操作与++操作分离。
  1. 补充3:string与vector自实现方式
    <1> string的存储元素只是字符,所以没有深拷贝,可以直接进行strcpy系列的函数使用
    <2> vector的存储数据可能是自定义类型,必须要进行深拷贝
    <3> string挪动数据使用的是下标的方式,因为size_t的下标类型所以要考虑头插的特殊情况处理
    <4> vector使用迭代器的方式实现数据挪动的操作,避免的string的类似问题
    <5>string的成员变量与对数据的维护方式没有使用迭代器
    <6> vector使用迭代器维护数据,进行数据操作,也因此导致迭代器失效这一情况的出现

6. operator[]的运算符重载

vector<T>& operator[](size_t pos)
{
	assert(pos < _finish - _start);

	return _start + pos;
}

const vector<T>& operator[](size_t pos) const
{
	assert(pos < _finish - _start);

	return _start + pos;
}

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

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

相关文章

34 | 到底可不可以使用join?

在实际生产中&#xff0c;关于 join 语句使用的问题&#xff0c;一般会集中在以下两类&#xff1a; 1. 我们 DBA 不让使用 join&#xff0c;使用 join 有什么问题呢&#xff1f; 2. 如果有两个大小不同的表做 join&#xff0c;应该用哪个表做驱动表呢&#xff1f; 今天这篇文…

【进程和线程】操作系统中的并发执行机制

目录 一、什么是进程(Process)&#xff1f; 进程的管理 进程调度(重点) 二、什么是线程(Thread)&#xff1f; 三、进程和线程的区别与联系 进程(Process) 线程(Thread) 总结比较 一、什么是进程(Process)&#xff1f; 进程和线程是操作系统中一个非常核心的话题&#…

鸿蒙Harmony应用开发—ArkTS-@Provide装饰器和@Consume装饰器:与后代组件双向同步

Provide和Consume&#xff0c;应用于与后代组件的双向数据同步&#xff0c;应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递&#xff0c;Provide和Consume摆脱参数传递机制的束缚&#xff0c;实现跨层级传递。 其中Provide装饰的变…

随笔】Git -- 常用命令(四)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

OpenHarmony实现一次开发多端部署分布式新闻客户端页面

分布式新闻客户端&#xff08;ArkTS&#xff09; 介绍 本篇Codelab基于栅格布局、设备管理和多端协同&#xff0c;实现一次开发&#xff0c;多端部署的分布式新闻客户端页面。主要包含以下功能&#xff1a; 展示新闻列表以及左右滑动切换新闻Tab。点击新闻展示新闻详情页。点…

产生死锁的四大条件

死锁 由于两个或两个以上的线程相互争夺对方的资源&#xff0c;而同时不释放自己的资源&#xff0c;导致所有线程同时被阻塞 产生死锁的四大条件 互斥条件&#xff1a;一个资源在同一时刻只能由一个运算单元&#xff08;进程、线程或协程&#xff09;占用&#xff08;排它性&…

停车场引导与道闸系统工程解析

停车场引导和道闸系统工程是一个综合性的智能车辆管理系统&#xff0c;它结合了现代电子信息技术&#xff0c;旨在实现安全、高效、自动化的停车管理。以下是该系统的主要组成部分及其功能&#xff1a; 1. 车牌识别技术&#xff1a;这是现代停车场管理的核心&#xff0c;能够自…

华清远见作业第五十三天——ARM(第七天)

代码 key_inc.h #ifndef __KEY_INC_H__ #define __KEY_INC_H__ #include "stm32mp1xx_gic.h" #include "stm32mp1xx_exti.h" #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h"void key1_it_config();void key2_it_config(…

C++概述

目录 一、C关键字&#xff08;63个&#xff09; 二、C几个关键点&#xff1a; 三、C语言缺陷一&#xff1a;命名冲突 四、C新概念&#xff1a;命名空间&#xff08;namespace&#xff09; 五、命名空间的嵌套&#xff1a; 六、展开命名空间&#xff1a;&#xff08;using …

【收藏】什么是API测试?这是我见过的最全的测试指南!

在最近的部署中&#xff0c;当我被问到“什么是API测试&#xff1f;”时&#xff0c;我正与客户一起制定API测试策略。那时我突然意识到&#xff0c;要描述API测试居然是一件如此具有挑战性的事情&#xff0c;即使你如实地描述了它&#xff0c;也往往听起来很无聊和复杂。 好吧…

FloodFill算法——岛屿数量

文章目录 题目解析算法解析代码解析 题目解析 岛屿数量 题目依旧是熟悉的配方&#xff0c;熟悉的味道&#xff0c;还是那个0还是那个1还是那个二维矩阵&#xff0c;这时候BFS和DFS闻着味就来了&#xff0c;我们来看一下这个题目&#xff0c;这个题目也很容易理解如下图有一个…

【No.14】蓝桥杯贪心法|最少硬币问题|活动安排问题(4)|翻硬币|快乐司机|防御力|答疑(C++)

算法优点 容易理解&#xff1a;生活常见 操作简单&#xff1a;在每一步都选局部最优 效率高&#xff1a;复杂度常常是O(1)的 算法缺点 局部最优不一定是全局最优 贪心算法&#xff08;Greedy algorithm&#xff09;&#xff0c;又称贪婪算法。是一种在每一步选择中都采取在…

教你在PC客户端中集成镭速高速传输插件

企业一直以来对文件传输的速度和安全性都有着严苛的要求&#xff0c;传统的FTP/HTTP传输方式因速度慢、易受网络延迟影响、数据包丢失等问题而不再适应现代企业的需求。镭速高速传输插件应运而生&#xff0c;为企业提供了一个快速、安全的文件传输新选择。本文将详细介绍如何在…

代码随想录算法训练营第day54|392.判断子序列 、 115.不同的子序列

目录 392.判断子序列 115.不同的子序列 392.判断子序列 力扣题目链接(opens new window) 给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字…

【WEEK4】 【DAY3】整合SSM框架之功能实现—修改、删除数据【中文版】

2024.3.20 Wednesday 接上文【WEEK4】 【DAY2】整合SSM框架之功能实现—总览、添加数据【中文版】 目录 7.6.修改功能7.6.1.修改BookController.java7.6.2.修改allBook.jsp7.6.3.新建updateBook.jsp7.6.4.修改MyBatis-config.xml7.6.5.运行 7.7.删除功能7.7.1.修改BookContro…

unicloud快速上手,unicloud项目创建以及项目创建注意事项

uniCloud快速上手 本项目地址https://gitee.com/qayrup/unicloud-demo 创建unicloud项目 新建一个uni项目,并选择启用unicloud,选择阿里云或腾讯云 阿里云和支付宝云都支持一个月免费的云,如果只想体验啥的,可以选择这两个, 但是需要注意,支付宝云需要配置跨域,否则很多云函…

ModuleNotFoundError: No module named ‘Crypto‘的解决办法

Crypto模块是什么 在Python中&#xff0c;Crypto模块&#xff08;有时也被称为pycrypto&#xff09;是一个强大的加密库&#xff0c;它提供了各种加密算法的实现&#xff0c;包括AES、DES、RSA等。 在Python 3中&#xff0c;由于pycrypto库与新版本的Python3不兼容&#xff0…

DES加密原理及python脚本

一、加密 1、秘钥处理 ​ DES算法会先对64位密钥进行处理生成48位子密钥后再参与到算法的轮操作中&#xff0c;在每一轮的迭代过程中&#xff0c;使用不同的子密钥。其中的处理包括置换选择、循环左移、压缩置换。 1.1 置换选择 DES秘钥有64位&#xff0c;其中每8位有一个校…

[HackMyVM]靶场 XMAS

kali:192.168.56.104 靶机:192.168.56.126 注意在/etc/hosts 添加 192.168.56.126 christmas.hmv # cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 kali2 192.168.223.131 dc-2 192.168.223.134 wordy 192.168.56.105 midn…

【嵌入式开发 Linux 常用命令系列 4.3 -- git add 时单独排除某个目录或者文件】

文章目录 git add 时单独排除某个目录或者文件使用 .gitignore 文件使用命令行排除文件或目录 git add 时单独排除某个目录或者文件 在使用 git add 命令时&#xff0c;如果你想要排除特定的目录或文件&#xff0c;可以使用 .gitignore 文件或使用路径规范来指定不想添加的文件…