【C++庖丁解牛】vector容器的简易模拟实现(C++实现)(最后附源码)

这篇具有很好参考价值的文章主要介绍了【C++庖丁解牛】vector容器的简易模拟实现(C++实现)(最后附源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【C++庖丁解牛】vector容器的简易模拟实现(C++实现)(最后附源码),c++入门到精通,c++,开发语言,数据库
🍁你好,我是 RO-BERRY
📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识
🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油
【C++庖丁解牛】vector容器的简易模拟实现(C++实现)(最后附源码),c++入门到精通,c++,开发语言,数据库


前言

我们前面介绍了vector容器的概念以及对其基本使用进行了介绍,如果你在这里不知道vector是什么以及不知道如何使用的话,可以进入本人主页,在C++专栏里有介绍

为了对小白友好,在这我简单介绍一下

C++中的vector是一个动态数组容器,可以存储不同类型的元素。它提供了一系列的成员函数来方便地操作和管理数组。

以下是C++ vector容器的一些特点和功能:

  1. 动态大小:vector的大小可以根据需要动态调整,可以在运行时添加或删除元素。
  2. 随机访问:可以通过索引直接访问vector中的元素,支持常数时间的随机访问。
  3. 自动内存管理:vector会自动处理内存分配和释放,无需手动管理内存。
  4. 插入和删除:可以在任意位置插入或删除元素,vector会自动调整其他元素的位置。
  5. 迭代器支持:可以使用迭代器遍历vector中的元素。
  6. 动态增长:当vector的容量不足时,会自动重新分配更大的内存空间,以容纳更多的元素。
  7. 元素访问:可以使用下标运算符[]或at()函数来访问元素,也可以使用front()和back()函数分别获取第一个和最后一个元素。

本章节主要对vector容器的手撕实现其简单功能

vector容器代码实现

内部成员简介

命名空间实现与库里vector的隔绝,实现自定义vector文章来源地址https://www.toymoban.com/news/detail-839724.html

namespace A
{
	template<class T>   //模版实现vector<类型>
	class vector        //vector功能实现
	{
	public:
		// Vector的迭代器是一个原生指针
		typedef T* iterator;
		typedef const T* const_iterator;
		.......           //函数接口实现
		.......
	private:
		iterator _start;		// 指向数据块的开始
		iterator _finish;		// 指向有效数据的尾
		iterator _endOfStorage;  // 指向存储容量的尾
	};
}

构造函数

		vector()
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{}

		vector(size_t n, const T& value = T())
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{
			reserve(n);
			while (n--)
			{
				push_back(value);
			}
		}
	* 理论上讲,提供了vector(size_t n, const T& value = T())之后
	* vector(int n, const T& value = T())就不需要提供了,但是对于:
	* vector<int> v(10, 5);
	* 编译器在编译时,认为T已经被实例化为int,而10和5编译器会默认其为int类型
	* 就不会走vector(size_t n, const T& value = T())这个构造方法,
	* 最终选择的是:vector(InputIterator first, InputIterator last)
	* 因为编译器觉得区间构造两个参数类型一致,因此编译器就会将InputIterator实例化为int
	* 但是10和5根本不是一个区间,编译时就报错了
	* 故需要增加如下构造方法
vector(int n, const T& value = T())
	: _start(new T[n])
	, _finish(_start + n)
	, _endOfStorage(_finish)
{
	for (int i = 0; i < n; ++i)
	{
		_start[i] = value;
	}
}

拷贝函数

		// 若使用iterator做迭代器,会导致初始化的迭代器区间[first,last)只能是vector的迭代器
		// 重新声明迭代器,迭代器区间[first,last)可以是任意容器的迭代器
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		vector(const vector<T>& v)
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{
			reserve(v.capacity());
			iterator it = begin();
			const_iterator vit = v.cbegin();
			while (vit != v.cend())
			{
				*it++ = *vit++;
			}
			_finish = it;
		}

		vector<T>& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}

析构函数

		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _endOfStorage = nullptr;
			}
		}

迭代器相关

		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator cbegin() const
		{
			return _start;
		}

		const_iterator cend() const
		{
			return _finish;
		}

容量相关

		size_t size() const
		{
			return _finish - _start;
		}

		size_t capacity() const
		{
			return _endOfStorage - _start;
		}

		bool empty() const
		{
			return _start == _finish;
		}

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t oldSize = size();
				// 1. 开辟新空间
				T* tmp = new T[n];

				// 2. 拷贝元素
				// 这里直接使用memcpy会有问题吗?同学们思考下
				//if (_start)
				//	memcpy(tmp, _start, sizeof(T)*size);

				if (_start)
				{
					for (size_t i = 0; i < oldSize; ++i)
						tmp[i] = _start[i];

					// 3. 释放旧空间
					delete[] _start;
				}

				_start = tmp;
				_finish = _start + oldSize;
				_endOfStorage = _start + n;
			}
		}

		void resize(size_t n, const T& value = T())
		{
			// 1.如果n小于当前的size,则数据个数缩小到n
			if (n <= size())
			{
				_finish = _start + n;
				return;
			}

			// 2.空间不够则增容
			if (n > capacity())
				reserve(n);

			// 3.将size扩大到n
			iterator it = _finish;
			_finish = _start + n;
			while (it != _finish)
			{
				*it = value;
				++it;
			}
		}

元素访问

		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}

		const T& operator[](size_t pos)const
		{
			assert(pos < size());
			return _start[pos];
		}

		T& front()
		{
			return *_start;
		}

		const T& front()const
		{
			return *_start;
		}

		T& back()
		{
			return *(_finish - 1);
		}

		const T& back()const
		{
			return *(_finish - 1);
		}

vector的修改操作

		void push_back(const T& x)
		{
			insert(end(), x);
		}

		void pop_back()
		{
			erase(end() - 1);
		}

		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endOfStorage, v._endOfStorage);
		}

		iterator insert(iterator pos, const T& x)
		{
			assert(pos <= _finish);

			// 空间不够先进行增容
			if (_finish == _endOfStorage)
			{
				//size_t size = size();
				size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2;
				reserve(newCapacity);

				// 如果发生了增容,需要重置pos
				pos = _start + size();
			}

			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}

			*pos = x;
			++_finish;
			return pos;
		}

		// 返回删除数据的下一个数据
		// 方便解决:一边遍历一边删除的迭代器失效问题
		iterator erase(iterator pos)
		{
			// 挪动数据进行删除
			iterator begin = pos + 1;
			while (begin != _finish) 
			{
				*(begin - 1) = *begin;
				++begin;
			}

			--_finish;
			return pos;
		}

源代码

#pragma once

#include <iostream>
using namespace std;
#include <assert.h>


namespace A
{
	template<class T>
	class vector
	{
	public:
		// Vector的迭代器是一个原生指针
		typedef T* iterator;
		typedef const T* const_iterator;

		///
		// 构造和销毁
		vector()
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{}

		vector(size_t n, const T& value = T())
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{
			reserve(n);
			while (n--)
			{
				push_back(value);
			}
		}

		/*
		* 理论上将,提供了vector(size_t n, const T& value = T())之后
		* vector(int n, const T& value = T())就不需要提供了,但是对于:
		* vector<int> v(10, 5);
		* 编译器在编译时,认为T已经被实例化为int,而10和5编译器会默认其为int类型
		* 就不会走vector(size_t n, const T& value = T())这个构造方法,
		* 最终选择的是:vector(InputIterator first, InputIterator last)
		* 因为编译器觉得区间构造两个参数类型一致,因此编译器就会将InputIterator实例化为int
		* 但是10和5根本不是一个区间,编译时就报错了
		* 故需要增加该构造方法
		*/
		vector(int n, const T& value = T())
			: _start(new T[n])
			, _finish(_start + n)
			, _endOfStorage(_finish)
		{
			for (int i = 0; i < n; ++i)
			{
				_start[i] = value;
			}
		}

		// 若使用iterator做迭代器,会导致初始化的迭代器区间[first,last)只能是vector的迭代器
		// 重新声明迭代器,迭代器区间[first,last)可以是任意容器的迭代器
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		vector(const vector<T>& v)
			: _start(nullptr)
			, _finish(nullptr)
			, _endOfStorage(nullptr)
		{
			reserve(v.capacity());
			iterator it = begin();
			const_iterator vit = v.cbegin();
			while (vit != v.cend())
			{
				*it++ = *vit++;
			}
			_finish = it;
		}

		vector<T>& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}

		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _endOfStorage = nullptr;
			}
		}

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

		iterator end()
		{
			return _finish;
		}

		const_iterator cbegin() const
		{
			return _start;
		}

		const_iterator cend() const
		{
			return _finish;
		}

		//
		// 容量相关
		size_t size() const
		{
			return _finish - _start;
		}

		size_t capacity() const
		{
			return _endOfStorage - _start;
		}

		bool empty() const
		{
			return _start == _finish;
		}

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t oldSize = size();
				// 1. 开辟新空间
				T* tmp = new T[n];

				// 2. 拷贝元素
				// 这里直接使用memcpy会有问题吗?同学们思考下
				//if (_start)
				//	memcpy(tmp, _start, sizeof(T)*size);

				if (_start)
				{
					for (size_t i = 0; i < oldSize; ++i)
						tmp[i] = _start[i];

					// 3. 释放旧空间
					delete[] _start;
				}

				_start = tmp;
				_finish = _start + oldSize;
				_endOfStorage = _start + n;
			}
		}

		void resize(size_t n, const T& value = T())
		{
			// 1.如果n小于当前的size,则数据个数缩小到n
			if (n <= size())
			{
				_finish = _start + n;
				return;
			}

			// 2.空间不够则增容
			if (n > capacity())
				reserve(n);

			// 3.将size扩大到n
			iterator it = _finish;
			_finish = _start + n;
			while (it != _finish)
			{
				*it = value;
				++it;
			}
		}

		///
		// 元素访问
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}

		const T& operator[](size_t pos)const
		{
			assert(pos < size());
			return _start[pos];
		}

		T& front()
		{
			return *_start;
		}

		const T& front()const
		{
			return *_start;
		}

		T& back()
		{
			return *(_finish - 1);
		}

		const T& back()const
		{
			return *(_finish - 1);
		}
		/
		// vector的修改操作
		void push_back(const T& x)
		{
			insert(end(), x);
		}

		void pop_back()
		{
			erase(end() - 1);
		}

		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endOfStorage, v._endOfStorage);
		}

		iterator insert(iterator pos, const T& x)
		{
			assert(pos <= _finish);

			// 空间不够先进行增容
			if (_finish == _endOfStorage)
			{
				//size_t size = size();
				size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2;
				reserve(newCapacity);

				// 如果发生了增容,需要重置pos
				pos = _start + size();
			}

			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}

			*pos = x;
			++_finish;
			return pos;
		}

		// 返回删除数据的下一个数据
		// 方便解决:一边遍历一边删除的迭代器失效问题
		iterator erase(iterator pos)
		{
			// 挪动数据进行删除
			iterator begin = pos + 1;
			while (begin != _finish) {
				*(begin - 1) = *begin;
				++begin;
			}

			--_finish;
			return pos;
		}
	private:
		iterator _start;		// 指向数据块的开始
		iterator _finish;		// 指向有效数据的尾
		iterator _endOfStorage;  // 指向存储容量的尾
	};
}

/// /
/// 对模拟实现的vector进行严格测试
void TestBitVector1()
{
	A::vector<int> v1;
	A::vector<int> v2(10, 5);

	int array[] = { 1,2,3,4,5 };
	A::vector<int> v3(array, array + sizeof(array) / sizeof(array[0]));

	A::vector<int> v4(v3);

	for (size_t i = 0; i < v2.size(); ++i)
	{
		cout << v2[i] << " ";
	}
	cout << endl;

	auto it = v3.begin();
	while (it != v3.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

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

void TestBitVector2()
{
	A::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	cout << v.size() << endl;
	cout << v.capacity() << endl;
	cout << v.front() << endl;
	cout << v.back() << endl;
	cout << v[0] << endl;
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

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

	v.insert(v.begin(), 0);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	v.erase(v.begin() + 1);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

到了这里,关于【C++庖丁解牛】vector容器的简易模拟实现(C++实现)(最后附源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++庖丁解牛】二叉搜索树(Binary Search Tree,BST)

    🍁你好,我是 RO-BERRY 📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 若它的左子树不为空,

    2024年03月28日
    浏览(43)
  • 【C++庖丁解牛】面向对象的三大特性之一多态 | 抽象类 | 多态的原理 | 单继承和多继承关系中的虚函数表

    🍁你好,我是 RO-BERRY 📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 需要声明的,本节课件中的代码及解释都是在vs2013下的x86程序中,涉及的指针都是4bytes。如果要其他平台

    2024年04月10日
    浏览(41)
  • 庖丁解牛函数知识---C语言《2》

    目录 前言: 1.嵌套调用函数 2.链式访问 3.函数的声明与定义 4.*递归 5.递归与非递归 ❤博主CSDN:啊苏要学习   ▶专栏分类:C语言◀   C语言的学习,是为我们今后学习其它语言打好基础,C生万物!   开始我们的C语言之旅吧!✈   在第一篇的基础上,我们接着学习函数相关的

    2024年02月02日
    浏览(41)
  • 庖丁解牛函数知识---C语言《1》

    目录 前言: 1.程序中的函数 2.库函数的学习和使用 3.自定义函数 4.传值调用与传址调用 5.形参与实参 6.练习---二分查找函数 ❤博主CSDN:啊苏要学习   ▶专栏分类:C语言◀   C语言的学习,是为我们今后学习其它语言打好基础,C生万物!   开始我们的C语言之旅吧!✈   在计

    2024年02月02日
    浏览(44)
  • 【数据结构】 队列详解!庖丁解牛般细致讲解!

    🎥 屿小夏 : 个人主页 🔥个人专栏 : 数据结构解析 🌄 莫道桑榆晚,为霞尚满天! 什么是队列?队列有什么样的特性?它的应用场景有哪些? 本文会对队列这种数据结构进行进行庖丁解牛般的讲解,让你彻底学会数据结构! 队列是一种常见的数据结构,它按照先进先出

    2024年02月06日
    浏览(33)
  • 【数据结构---排序】庖丁解牛式剖析常见的排序算法

    排序在我们生活中处处可见,所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 常见的排序算法可以分为四大类:插入排序,选择排序,交换排序,归并排序;其中,插入排序分为直接插入排序和希尔排序;选择排序分为直接

    2024年02月16日
    浏览(44)
  • C生万物 | 操作符汇总大全【庖丁解牛,精细讲解】

    本篇博客全站热榜最高排名:2 因为MarkDown的语法,所以用图片的形式显示 对于算术操作符而言有上面这五种,对于前面的【+】、【-】、【*】来说操作数可以是整数或者浮点数 对于【/】来说,叫做 整除 ,结果就是我们在数学中说到的 商 。若是两边都是整数,则执行执行

    2023年04月08日
    浏览(38)
  • 【数据结构】庖丁解牛,图文结合带你轻松上手带头循环链表

    君兮_的个人主页 勤时当勉励 岁月不待人 C/C++ 游戏开发 Hello,米娜桑们,这里是君兮_,我们今天接着上回的单链表来讲讲带头双向循环链表,这种链表也是我们在实际应用中最常用的几种链表之一,学好这种链表是是非常重要的,我会尽量用通俗易懂的文字配合逻辑图来帮助

    2024年02月14日
    浏览(31)
  • 【庖丁解牛】vue-element-admin前端CRUD通用操作组件详解,对,核心就是crud.js文件

    vue-element-admin 框架之所以能够快速定制应用,得益于其通配的CRUD操作,属性配置多样化且个性化,能够满足绝大部分开发需求,也方便了代码生成。 可以深入学习重点源文件是: src/components/Crud/crud.js ,一共 863 行代码,以及下图中其它四个vue组件,形成了对通用CRUD操作的高

    2024年01月18日
    浏览(42)
  • 【C++】vector容器的模拟实现

    目录 一,框架设计 二,构造函数 三,析构函数 四,赋值运算符 五,容器接口的实现 1,迭代器实现 2,“ [] ”运算符的实现 3,swap交换和resize重设大小 4,insert插入和erase删除 介绍:         本文,我们重点实现vector容器的用法,这里要注意的是vector容器可以接纳任意类

    2024年02月02日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包