【STL学习】(4)vector的模拟

本文围绕C++中vector常用接口的模拟实现展开。先介绍前置知识,包括看源码的技巧和内存池概念。接着详细模拟vector成员变量、默认成员函数、遍历方式、reserve和resize操作、插入与删除操作,还着重强调了迭代器失效问题及解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本文将模拟实现vector的常用功能,目的在于更深入理解vector。

一、前置知识

  1. 在模拟之前先对vector的结构和常用接口学习,有一个大致了解。
  2. 看源码,本文参考的源码是SGI版本的stl3.0。
    • 技巧:
      • 看源码不要一行一行的看,要先看框架,了解整体框架
      • 看源码要学会猜,根据单词的意思去猜它想表达什么。规范的代码,每一个名字都有它的含义。
      • 总结:一看框架;二去猜(带着猜想,去验证)。
    • 看框架的步骤:
      • 先看成员变量
      • 再看成员函数
  3. 参考vector的源码:
    • vector的成员变量:是三个原生指针变量(设为原生指针类型有什么好处,在模拟时讲解)在这里插入图片描述
    • vector的成员函数:vector的常用接口讲解链接
  4. STL中的容器因为需要频繁的申请和释放空间,所以STL中提供了内存池(allocator类模板),内存池的本质是先在堆区中申请一定的空间留作备用,当有新的内存需求时,就从内存池中分出一块内存块,若内存块不够再继续申请新的内存,这样可以提高内存分配的效率。现阶段我们只是简单模拟vector,所以我们这没有使用内存池,而是直接在堆区申请空间,后期会讲解内存池的。

二、vector常用接口的模拟

1、vector的成员变量

vector的成员变量是三个原生指针T*:

  1. _start:开始位置,即指向第一个元素的位置
  2. _finish:结束位置,即指向最后一个元素的下一个位置
  3. _end_of_storage:存储结束位置在这里插入图片描述
    虽然vector使用的是三个原生指针,但是可以通过指针运算得到size和capacity。

代码示例:

//为了避免与库中的vector冲突,将其封装在wjs的命名空间中
namespace wjs
{
   
   
	//类模板的实现和定义不分离,后续学到模板进阶会讲解!
	template<class T>
	class vector
	{
   
   
	public:
		typedef T* iterator;
		//获取容器中的元素个数
		size_t size()const//内部不改变成员变量,建议加上const——普通对象和const对象都可以调用
		{
   
   
			//指针-指针=两者之间的元素个数
			return _finish - _start;
		}

		//获取为当前容器分配的存储空间
		size_t capacity()const//内部不改变成员变量,建议加上const——普通对象和const对象都可以调用
		{
   
   
			//指针-指针=两者之间的元素个数
			return _end_of_storage - _start;
		}
	private:
		iterator _start;//开始位置,指向第一个元素
		iterator _finish;//结束位置,指向最后一个元素的下一个位置
		iterator _end_of_storage;//指向存储结束位置
	};
}

tip:

  1. 使用命名空间将我们模拟实现的vector封装,避免命名冲突。
  2. 类模板的定义与实现不分离,后续在模板进阶讲解。
  3. size和capacity可以通过指针-指针得到,注意指针-指针运算有一个前提是:物理存储空间是连续的。
  4. const成员:
    • const修饰的是*this,即const成员函数的内部不能修改成员变量
    • 建议只要成员函数内部不修改成员变量,都应该加const,这样普通对象和const对象都可以调用

2、vector的默认成员函数

构造函数

  • 构造函数:创建类对象时,编译器自动调用,给成员变量赋初值
  • 类的成员变量建议在初始化列表初始化
  • 成员变量为内置类型需要我们手动初始化,不然为随机值;成员变量为自定义类型不初始化,会去调用它的默认构造(建议每个类,都要有一个默认构造)
  • vector的常用构造函数:
    • 默认构造函数:一般使用最多,构造一个空的vector,即将每个成员初始化为空
    • 构造并初始化n个val:先初始化成员变量,再复用reserve开n的空间,最后再通过尾插将val插入容器
    • 使用迭代器初始化构造:先初始化成员变量,再将迭代器区间的数据尾插入容器

析构函数

  • 析构函数:对象销毁时,编译器自动调用,完成对象中资源的清理
  • 编译器生成的析构函数,对内置类型不做处理,自定义类型会去调用它的析构函数
  • 当类涉及动态资源的申请时,就需要显式实现析构释放资源。

赋值重载函数

  • 赋值重载函数:已经存在的两个对象复制拷贝
  • 当类涉及资源管理时,就需要自己显示实现完成深拷贝,编译器默认生成的赋值重载函数只能完成浅拷贝
  • 赋值重载深拷贝的现代写法:让形参去调用拷贝构造,去帮我们开空间拷贝数据,之后与形参交换(函数结束后形参销毁,也顺便帮我们把旧空间释放了)
  • 现代写法无法避免自己给自己赋值的情况,当现实中也很少会出现

拷贝构造函数

  • 拷贝构造函数:用一个已经存在的对象初始化另一个对象
  • 注意:拷贝构造只有一个参数,并且必须是本类型的引用(使用传值会引发无限递归)
  • 编译器默认生成的拷贝构造也是只能完成浅拷贝,所以当类涉及资源管理时,就需要自己显式实现完成深拷贝
  • 拷贝构造深拷贝的现代写法:自己开空间,自己拷贝数据

总结:当类涉及资源管理时,拷贝构造、赋值重载、析构都需要显式实现。

//为了避免与库中的vector冲突,将其封装在wjs的命名空间中
namespace wjs
{
   
   
	//类模板的实现和定义不分离,后续学到模板进阶会讲解!
	template<class T>
	class vector
	{
   
   
	public:
		//默认构造函数
		vector()
			//初始化列表:成员变量定义的地方,建议在初始化列表初始化成员变量
			//成员变量为内置类型不初始化,为随机值
			:_start(nullptr),
			_finish(nullptr),
			_end_of_storage(nullptr)
		{
   
   }
		//构造并初始化n个val
		vector(size_t n, const T& val = T())//T()调用构造函数
			//初始化成员变量
			:_start(nullptr),
			_finish(nullptr),
			_end_of_storage(nullptr)
		{
   
   
			//复用reserve,开空间
			reserve(n);
			//复用push_back,尾插n个val
			for (size_t i = 0; i < n; ++i)
			{
   
   
				push_back(val);
			}
		}
		//使用迭代器区间初始化
		//类模板的成员函数也可以是函数模板
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
			//初始化成员变量
			:_start(nullptr),
			_finish(nullptr),
			_end_of_storage(nullptr)
		{
   
   
			//复用push_back,将迭代器区间[first,last)的数据尾插进容器
			while (first != last)
			{
   
   
				push_back(*first);
				++first;
			}
		}
		//当wjs::vector<int> v(10, 1);报错——》 error C2100: 非法的间接寻址
		//函数重载,调用时会走最匹配的,wjs::vector<int> v(10, 1)两个参数类型都是int,所以他走使用迭代器构造
		//重载一个vector(int n, const T& val = T()),他就会走构造n个val
		//构造并初始化n个val
		vector(int n, const T& val = T())//T()调用构造函数
			//初始化成员变量
			:_start(nullptr),
			_finish(nullptr),
			_end_of_storage(nullptr)
		{
   
   
			//复用reserve,开空间
			reserve(n);
			//复用push_back,尾插n个val
			for (int i = 0; i < n; ++i)
			{
   
   
				push_back(val);
			}
		}
		
		//交换两个vector对象
		void swap(vector<T>& v)
		{
   
   
			std::swap(_start, v._start);
			std::swap(_finish, v._finish)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值