c++的学习之路:13、vector(2)

本章主要是模拟实现vector,文章末附上代码,和源码。

目录

一、STL源码

二、构造与析构

三、迭代器与【】、size、capacity、empty

四、reserve与resize

五、push_back与pop_back

六、insert与erase

[七、测试 1](#七、测试 1)

八、代码

九、思维导图


一、STL源码

今天看的是STL30这个源码,他的vector点开是下图这种可以看出他也是调用了几个头文件,上面注释的就是一些开源声明,大概就是说是可以使用、增删查改甚至售卖,做出有用的修改也需要声明。

如下图二可以看出vector的参数是只有三个原生指针,这个就是从图一这里就可以看出是一个typede进行的重定义的。

他的size,capacity是如下图这样使用的,下面九不一一展示了,源码上传了可以自己看看。

二、构造与析构

如下方代码就是这个构造和析构,这里是放在了我的命名空间中以防和库里面的vector重复,这里是利用了缺省传值,在定义的时候直接赋值为nullptr然后进行一个空的初始化,然后如下方代码中vector(size_t n, const T& val = T())这一句就是利用一个匿名对象进行初始化,因为这样就可以直接使用模板进行构造,因为这样就可以使用不用的类型进行初始化对象,然后又利用了模板参数InputIterator进行初始化,这里就是如上篇文章中可以使用范围进行初始化,这里就是利用迭代器的原理,first就是迭代器的begin,last就是迭代器的end,也就是首和尾,然后进行构造,相当于缺省值的构造,这里面构造是需要开辟空间和插入数据,所以这里先写在这了,代码在后面文章, vector(int n, const T& val = T())有个这个是因为写入的值默认是整形,在构造时会进行隐形类型转换,size_t是无符号整形,需要转化,但是有类模板会优先使用这个就会造成野指针。

namespace ly

{

template<class T>

class vector

{

public:

typedef T* iterator;

typedef const T* const_iterator;

vector()

{}

vector(size_t n, const T& val = T())

{

reserve(n);

for (size_t i = 0; i < n; ++i)

{

push_back(val);

}

}

vector(int n, const T& val = T())

{

reserve(n);

for (int i = 0; i < n; ++i)

{

push_back(val);

}

}

template <class InputIterator>

vector(InputIterator first, InputIterator last)

{

while (first != last)

{

push_back(*first);

++first;

}

}

~vector()

{

delete\[\] _start;

_start = _finish = _end_of_storage = nullptr;

}

private:

iterator _start = nullptr;

iterator _finish = nullptr;

iterator _end_of_storage = nullptr;

};

}

三、迭代器与【】、size、capacity、empty

如下方代码所示,先看迭代器,在上文中就已经重定义了iterator是T*,所以这里begin就是直接返回start的指针,end就是finish,看过源码和之前模拟实现过string就会发现这里很好用,这里也是同样的有const,因为这里需要重载就是因为需要访问const类型的,size就是finish-start就能得出size,同理capacity就是end_of_storage - start,enpty就是判断是否满了,这里就是start等于finish的时候就是满了,【】就是直接访问就可以了,在pos位置进行访问,这里也是如下方代码所示。

iterator begin()

{

return _start;

}

iterator end()

{

return _finish;

}

const_iterator begin() const

{

return _start;

}

const_iterator end() const

{

return _finish;

}

size_t size() const

{

return _finish - _start;

}

size_t capacity() const

{

return _end_of_storage - _start;

}

bool empty()

{

return _start == _finish;

}

T& operator\[\](size_t pos)

{

assert(pos < size());

return _startpos;

}

const T& operator\[\](size_t pos) const

{

assert(pos < size());

return _startpos;

}

四、reserve与resize

这里是和之前模拟实现string的时候差不多,判断容量是否够,不够减扩容,这里因为需要计算---finish地址,所以提前记录了size,然后创建一个空间,然后利用memcpy进行拷贝数据,在把旧的start释放了,在指向新的空间,在把finish和end_of_storage计算出来就好了,resize也是需要判断是否是缩容,缩容并不需要缩容,只需要删除数据就可以了,扩容也就是需要扩容后,再把数据初始化就可以了。

void reserve(size_t n)

{

if (n > capacity())

{

size_t sz = size();

T* tmp = new Tn;

if (_start)

{

memcpy(tmp, _start, sizeof(T) * size());

delete\[\] _start;

}

_start = tmp;

_finish = _start + sz;

_end_of_storage = _start + n;

}

}

void resize(size_t n, T val = T())

{

if (n < size())

{

_finish = _start + n;

}

else

{

if (n > capacity())

{

reserve(n);

}

while (_finish != _start + n)

{

*_finish = val;

++_finish;

}

}

}

五、push_back与pop_back

这里就是先判断是否满了,满了就先进行扩容,然后进行存入数据,没满就把数据直接存入,删除也就是直接--覆盖就好了。

void push_back(const T& x)

{

if (_finish == _end_of_storage)

{

reserve(capacity() == 0 ? 4 : capacity() * 2);

}

*_finish = x;

++_finish;

}

void pop_back()

{

assert(!empty());

--_finish;

}

六、insert与erase

如下方代码,这里实现的思路是先断言,pos位置是在start和finish之间,不是就报错,如果刚好数据满了就进行扩容,这里需要注意的是扩容的时候需要进行计算pos的绝对位置,也就是距离start的长度,在扩容后在进行计算pos位置,然后就是挪动数据与把数据存入,erase就不需要判断相等finish的时候,因为满了也可以删,然后在进行挪动覆盖,这个就是erase的实现。

iterator insert(iterator pos, const T& val)

{

assert(pos >= _start);

assert(pos <= _finish);

if (_finish == _end_of_storage)

{

size_t len = pos - _start;

reserve(capacity() == 0 ? 4 : capacity() * 2);

pos = _start + len;

}

iterator end = _finish - 1;

while (end >= pos)

{

*(end + 1) = *end;

--end;

}

*pos = val;

++_finish;

return pos;

}

void erase(iterator pos)

{

assert(pos >= _start);

assert(pos < _finish);

iterator start = pos + 1;

while (start != _finish)

{

*(start - 1) = *start;

++start;

}

--_finish;

}

七、测试 1

这里就进行测试一下上面写的代码是否有错,如下图的代码就是测试push_back与pop_back的写发,然后这里也是测试了下迭代器的使用,for的实现就是迭代器。

void Print(const vector<int>& v)

{

for (auto vi : v)

{

cout << vi << ' ';

}

cout << endl;

}

void Test1()

{

vector<int> v1;

v1.push_back(1);

v1.push_back(2);

v1.push_back(3);

v1.push_back(4);

Print(v1);

v1.pop_back();

v1.pop_back();

Print(v1);

}

这里就是测试了下【】与利用模板进行范围的初始化

void Test2()

{

vector<int> v1;

v1.push_back(1);

v1.push_back(2);

v1.push_back(3);

v1.push_back(4);

Print(v1);

vector<int> v2(v1.begin(), v1.end());

Print(v2);

for (size_t i = 0; i < v2.size(); i++)

{

cout << v2i << ' ';

}

cout << endl;

}

这里是测试插入和删除,这里利用了find找到1和3进行头插和在3的位置插入,如下方所示。

void Test3()

{

vector<int> v1;

v1.push_back(1);

v1.push_back(2);

v1.push_back(3);

v1.push_back(4);

Print(v1);

auto pos = find(v1.begin(), v1.end(), 1);

v1.insert(pos, 0);

pos = find(v1.begin(), v1.end(), 3);

v1.insert(pos, 30);

Print(v1);

pos = find(v1.begin(), v1.end(), 0);

v1.erase(pos);

pos = find(v1.begin(), v1.end(), 30);

v1.erase(pos);

Print(v1);

}

八、代码

vector.h

复制代码
#pragma once
#include <iostream>
#include <vector>
#include <assert.h>
using namespace std;

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

		vector()
		{}

		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; ++i)
			{
				push_back(val);
			}
		}

		vector(int n, const T& val = T())
		{
			reserve(n);
			for (int i = 0; i < n; ++i)
			{
				push_back(val);
			}
		}

		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

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

		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin() const
		{
			return _start;
		}

		const_iterator end() const
		{
			return _finish;
		}

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

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

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

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

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

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)
				{
					memcpy(tmp, _start, sizeof(T) * size());
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + sz;
				_end_of_storage = _start + n;
			}
		}

		void resize(size_t n, T val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);
				}		
				while (_finish != _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}

		void push_back(const T& x)
		{
			if (_finish == _end_of_storage)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}
			*_finish = x;
			++_finish;
		}

		void pop_back()
		{
			assert(!empty());
			--_finish;
		}

		iterator insert(iterator pos, const T& val)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			if (_finish == _end_of_storage)
			{
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = val;
			++_finish;
			return pos;
		}

		void erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			iterator start = pos + 1;
			while (start != _finish)
			{
				*(start - 1) = *start;
				++start;
			}
			--_finish;
		}

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

	void Print(const vector<int>& v)
	{
		for (auto vi : v)
		{
			cout << vi << ' ';
		}
		cout << endl;
	}

	void Test1()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		Print(v1);
		v1.pop_back();
		v1.pop_back();
		Print(v1);
	}

	void Test2()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		Print(v1);
		vector<int> v2(v1.begin(), v1.end());
		Print(v2);
		for (size_t i = 0; i < v2.size(); i++)
		{
			cout << v2[i] << ' ';
		}
		cout << endl;
	}

	void Test3()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		Print(v1);
		auto pos = find(v1.begin(), v1.end(), 1);
		v1.insert(pos, 0);
		pos = find(v1.begin(), v1.end(), 3);
		v1.insert(pos, 30);
		Print(v1);
		pos = find(v1.begin(), v1.end(), 0);
		v1.erase(pos);
		pos = find(v1.begin(), v1.end(), 30);
		v1.erase(pos);
		Print(v1);
	}
}

test.cpp

复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "vector.h"

int main()
{
	ly::Test3();
}

九、思维导图

相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
2401_841495642 小时前
【操作系统】进程同步与互斥实验报告
c++·算法·操作系统·进程·并发·同步·互斥
fqbqrr2 小时前
2607C++,soui与安卓
c++·soui
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
a1117765 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习
我的xiaodoujiao5 小时前
API 接口自动化测试详细图文教程学习系列32--Allure测试报告2
python·学习·测试工具·pytest
fqbqrr6 小时前
2607C++,使用微软detours勾挂工具
c++