C++之STL容器-----Vector实现

C++之STL容器-----Vector实现

一、Vector容器底层实现简易设计

(Vector内存图纵览)
在这里插入图片描述

(实现代码总览)

namespace wanboss
{
	template<class T>
	class vector
	{
	public:
		typedef T* itetator;
		
		//构造函数
		vector()
			:_start(nullptr), 
			 _finish(nullptr), 
			 _end_of_storage(nullptr)//初始化成员变量。
		{	}

		//拷贝构造函数 vector v(v1)
		vector(const vector<T>& v)//this-->v, const vector<T>& v-->v1。
		{
			_start = new T[v.capacity()];//创建一块以v1为基准的内存。
			_finish = _start;//新内存中没有数据,因此将尾指针指向开始位置。
			_end_of_storage = _start + v.capacity();//相当于capacity().

			for (size_t i = 0; i < v.size(); ++i)
			{
				//将 v 中值逐个拷贝给新的空间中,v[i] 会调用 operator[]方法。
				*_finish = v[i];
				++_finish;//元素进来得移动尾指针。
			}
		}
		
		//析构函数
		~vector()
		{
			delete[] _start;//释放内存空间
			_start = _finish = _end_of_storage = nullptr;//将指针置为空。
		}
		
		//迭代器的开始位置
		itetator begin()
		{
			return _start;
		}
	
		//迭代器的尾巴位置
		itetator end()
		{
			return _finish;
		}
		
		//const-->非const可以访问,const也可访问。
		size_t size() const
		{
			return _finish - _start;
		}

		//const-->非const可以访问,const也可访问。
		size_t capacity()const
		{
			return _end_of_storage - _start;
		}
		
		//operator[] 重定义。
		T& operator[](size_t i)
		{
			return *(_start + i);
		}
	
		//const-->非const可以访问,const也可访问。
		const T& operator[](size_t i)const
		{
			return *(_start + i);
		}
		
		//------------------------传统写法---------------------------------
		//v1(v2)
		vector<T>& operator=(const vector<T>& v)
		{
			if (this != &v)//防止 s1 = s1 防止降低效率。
			{
				delete[] _start;//释放原来的内存空间
				//基于 v2 创建一块新空间,并让原来旧空间指针指向。
				_start = new T[v.capacity()];
				//memcpy,按字节拷贝,将 v2 的内容拷贝给 v1。
				memcpy(_start, v._start, sizeof(T) * v.size());
			}
			return *this;
		}
		//------------------------传统写法---------------------------------

		//------------------------现代写法---------------------------------
		vector<T>& operator=(const vector<T>& v)
		{
			swap(_start, v._start);
			swap(_finish, v._finish);
			swap(_end_of_storage, v._end_of_storage);

			return *this;
		}
		//------------------------现代写法---------------------------------

		//reserve() 是改变容量的
		void reserve(size_t n)// n-->就是新的容量。
		{
			if (n > capacity())//如果 新空间大于总容量,增容。
			{
				//在进行内存操作之前,保存当前的大小,防止丢失。
				size_t old_size = size();

				T* temp = new T[n];//开辟一块临时空间

				/*
				如果 _start 为 nullptr,则表示当前 vector 是空的,没有任何已分配的内存,
				则无需进行以下的内存复制和释放操作,因为没有数据需要被复制,也没有内存需要被释放。
				*/
				if (_start)//
				{
					//memcpy(temp, _start, sizeof(T)* old_size);//按字节进行拷贝,属于浅拷贝
					for (size_t i = 0; i < old_size; ++i)
						temp[i] = _start[i];//调用的是 T 的operator=,属于深拷贝。

					delete[] _start;//释放原来的空间。
				}
				_start = temp;//将旧的指向变成新的空间指向。
				_finish = temp + old_size;//新的空间指向 + 旧的数据大小 = 新的空间数据结尾
				_end_of_storage = temp + n;//新的空间指向 + 旧的空间容量 = 新的空间容量。
			}

		}

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

		void push_back(const T& x)
		{
			//if (_finish == _end_of_storage)//首先判断,容量是否够?
			//{
			//	size_t newcapacoty = capacity() == 0 ? 2 : capacity() * 2;//不够,增容。
			//	reserve(newcapacoty);
			//}
			//*_finish = x;//尾插,将_finish指向的空间填入尾插的值
			//++_finish;//更新尾指针。
			insert(_finish, x);
		}

		void pop_back()
		{
			//if (_finish >= _start)
			//	_finish--;

			//assert(_finish >= _start);//加入一个断言比较符合规定。
			erase(_finish - 1);
		}

		void insert(itetator pos, const T& x)//表示插入一个元素
		{
			assert(pos <= _finish);//表示可以尾插
			if (_finish == _end_of_storage)
			{
				size_t len = pos - _start;//记录pos在增容前,与头指针距离。
				size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
				reserve(newcapacity);

				pos = _start + len;//更新pos在增容后,新空间指向问题。
			}

			itetator end = _finish - 1;//尾部第一个数。
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}

			*pos = x;
			++_finish;
		}

		itetator erase(itetator pos)
		{
			if (_end_of_storage != _start)
			{
				assert(pos < _finish&& pos >= _start);
				itetator newstart = pos;
				while (newstart < _finish)
				{
					*newstart = *(newstart + 1);
					++newstart;
				}

				--_finish;
				return pos;
			}
			else
			{
				return nullptr;
			}
		}
	private:
		itetator _start;
		itetator _finish;
		itetator _end_of_storage;
	};
}

二、Verctor类实现细节

2.1 构造函数拷贝构造析构函数

		//---------------------构造函数-----------------------------------
		/*一般的构造函数对成员变量进行初始化工作,这里的成员变量是指针,一般让其指向空*/
		vector()
			:_start(nullptr), 
			 _finish(nullptr), 
			 _end_of_storage(nullptr)//初始化成员变量。
		{	}
		//---------------------构造函数-----------------------------------

		//---------------------拷贝构造函数-----------------------------------
		//拷贝构造函数 vector v2(v1)
		/*碰到拷贝构造函数,就可以想到 Vector v2(v1) 这个示例,主要是分清楚:
		v1 和 v2 在拷贝构造中代表这谁。技巧:谁基于谁才创建的----> v2 是基于 v1 创建的*/
		vector(const vector<T>& v)//this-->v2, const vector<T>& v-->v1。
		{
			_start = new T[v.capacity()];//创建一块以v1为基准的内存。
			//this->_start = new T[v.capacity()];
			_finish = _start;//新内存中没有数据,因此将尾指针指向开始位置。
			_end_of_storage = _start + v.capacity();//相当于capacity().

			for (size_t i = 0; i < v.size(); ++i)
			{
				//将 v 中值逐个拷贝给新的空间中,v[i] 会调用 operator[]方法。
				*_finish = v[i];
				++_finish;//元素进来得移动尾指针。
			}
		}
		//---------------------拷贝构造函数-----------------------------------

		//析构函数
		~vector()
		{
			delete[] _start;//释放内存空间
			_start = _finish = _end_of_storage = nullptr;//将指针置为空。
		}

针对拷贝构造函数,绘制思维图理解。
在这里插入图片描述

2.2 迭代器相关—begin( )、end( )、size( )、capacity( )。

( 迭代器 浅层可以认为是 一个指针

//迭代器的开始位置
itetator begin()
{
	return _start;
}

//迭代器的尾巴位置
itetator end()
{
	return _finish;
}

//const-->非const可以访问,const也可访问。
size_t size() const
{
	return _finish - _start;
}

//const-->非const可以访问,const也可访问。
size_t capacity()const
{
	return _end_of_storage - _start;
}

从下图可以看出上述代码的关系图:
在这里插入图片描述

2.3 有关 operator 的运算符重载:operator = 和 operator [ ]

//operator[] 重定义。
T& operator[](size_t i)
{
	return *(_start + i);
}

//const-->非const可以访问,const也可访问。
const T& operator[](size_t i)const
{
	return *(_start + i);
}

//------------------------传统写法---------------------------------
//v1 = v2
vector<T>& operator=(const vector<T>& v)
{
	if (this != &v)//防止 s1 = s1 防止降低效率。
	{
		delete[] _start;//释放原来的内存空间
		//基于 v2 创建一块新空间,并让原来旧空间指针指向。
		_start = new T[v.capacity()];
		//memcpy,按字节拷贝,将 v2 的内容拷贝给 v1。
		memcpy(_start, v._start, sizeof(T) * v.size());
	}
	return *this;
}
//------------------------传统写法---------------------------------

//------------------------现代写法---------------------------------
vector<T>& operator=(const vector<T>& v)
{
	swap(_start, v._start);
	swap(_finish, v._finish);
	swap(_end_of_storage, v._end_of_storage);

	return *this;
}
//------------------------现代写法---------------------------------

有关于const问题:(可参考这篇博客–关于const修饰成员变量
在这里插入图片描述
关于运算符重载问题
在这里插入图片描述

2.4 reserve( )resize( ) 函数

reserve() : 改变容量(capacity())、resize() :改变有效字符大小个数(size())

//reserve() 是改变容量的
void reserve(size_t n)// n-->就是新的容量。
{
	if (n > capacity())//如果 新空间大于总容量,增容。
	{
		//在进行内存操作之前,保存当前的大小,防止丢失。
		size_t old_size = size();

		T* temp = new T[n];//开辟一块临时空间

		/*
		如果 _start 为 nullptr,则表示当前 vector 是空的,没有任何已分配的内存,
		则无需进行以下的内存复制和释放操作,因为没有数据需要被复制,也没有内存需要被释放。
		*/
		if (_start)//
		{
			//memcpy(temp, _start, sizeof(T)* old_size);//按字节进行拷贝,属于浅拷贝
			for (size_t i = 0; i < old_size; ++i)
				temp[i] = _start[i];//调用的是 T 的operator=,属于深拷贝。

			delete[] _start;//释放原来的空间。
		}
		_start = temp;//将旧的指向变成新的空间指向。
		_finish = temp + old_size;//新的空间指向 + 旧的数据大小 = 新的空间数据结尾
		_end_of_storage = temp + n;//新的空间指向 + 旧的空间容量 = 新的空间容量。
	}

}

void resize(size_t n, const T& val = T())//改变有效内容的大小,并将新的内容进行初始化。
{
	if (n < size())//
	{
		_finish = _start + n;
	}
	else
	{
		if (n > capacity())
		{
			 reserve(n);
		}
		
		itetator it = _start;
		//这一步比较关键,因为resize他是改变有效内容的大小并对新的空间进行初始化。
		//因此是在_finish之后添加,可以保证 n < size()时,不会改变原有内容。

		while (_finish < _start + n)		
		{
			*_finish = val;
			++_finish;
		}
	}
}

关于reserve( )的设计思绪图:
在这里插入图片描述
面试点:为什么reserve在进行拷贝的时候要使用代码上的形式,使用memcpy函数不行?
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值