【C++】vector类模拟实现

vector类模拟实现

  • 一、vector类的成员变量
  • 二、vector类的接口实现
    • 2.1 构造函数
    • 2.2 析构函数
    • 2.3 size和capacity
    • 2.4 扩容
      • 2.4.1 reserve扩容
      • 2.4.2 resize扩容
    • 2.5 尾插
    • 2.6 尾删
    • 2.7 []重载
    • 2.8 迭代器和const迭代器
    • 2.9 拷贝构造
      • 2.9.1 现代写法
        • 2.9.1.1 迭代器版构造函数❗️❗️
        • 2.9.1.2 迭代器的分类❗️❗️
    • 2.10 赋值拷贝
      • 2.10.1 现代写法
    • 2.11 插入
    • 2.12 删除

一、vector类的成员变量

跟以前的成员变量不同,vector的成员变量为:

	iterator _start;// 数据开始
	iterator _finish;// 数据结束
	iterator _endofstorage;// 空间结尾

iteratortypedef出来的:

typedef T* iterator;

三个成员变量的含义:
_start指向数据的开始,_finish指向结束位置的下一个位置,_endofstorage指向整个空间的最后。
【C++】vector类模拟实现_第1张图片

二、vector类的接口实现

2.1 构造函数

		// 构造函数
		vector()
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{}

2.2 析构函数

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

2.3 size和capacity

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

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

指针相减就代表中间的数据个数

2.4 扩容

2.4.1 reserve扩容

		void reserve(size_t n)
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					size_t sz = size();
					T* tmp = new T[n];
					if (_start)
					{
						//memcpy(tmp, _start, sizeof(T) * sz); 不能这么写
						for (size_t i = 0; i < sz; i++)
						{
							tmp[i] = _start[i];
						}
						delete[]_start;
					}
					_start = tmp;
					_finish = _start + sz;
					_endofstorage = _start + n;
				}
			}
		}

需要注意的细节:
1️⃣ 初始空间的大小要提前保存(新空间size会改变)
2️⃣ 要注意改变三个成员函数
3️⃣不能使用memcpy(浅拷贝)

假设现在定义一个vector的类:
【C++】vector类模拟实现_第2张图片

当我们扩容需要拷贝时,如果使用memcpy就会发生这种情况:
【C++】vector类模拟实现_第3张图片
调用析构函数就会引发崩溃

2.4.2 resize扩容

resize跟reserve比起来就多了一个初始化。

		void resize(size_t n, const T& val = T())// 匿名对象
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);
				}
				while (_finish != _endofstorage)
				{
					*_finish = val;
					_finish++;
				}
			}
		}

要注意的是单独使用T()时,当前行结束就会自动调用销毁。而使用const T& val = T()就可以延长生命周期。

2.5 尾插

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

2.6 尾删

		void pop_back()
		{
			assert(size() > 0);
			_finish--;
		}

2.7 []重载

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

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

2.8 迭代器和const迭代器

		iterator begin()
		{
			return _start;
		}

		const_iterator begin() const
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator end() const
		{
			return _finish;
		}

2.9 拷贝构造

		// 拷贝构造
		vector(const vector<T>& x)
		{
			_start = new T[x.capacity()];
			_finish = _start;
			_endofstorage = _start + x.capacity();
			// 拷贝
			for (size_t i = 0; i < x.size(); i++)
			{
				*_finish = x[i];
				_finish++;
			}
		}

2.9.1 现代写法

要写现代写法我们首先需要构造一个临时的vector类然后交换,而我们现在的构造函数没有参数,所以我们要再写一个构造函数

2.9.1.1 迭代器版构造函数❗️❗️

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

我们可以看到这种迭代器以前没有见过:

2.9.1.2 迭代器的分类❗️❗️

迭代器有很多类型:

只读迭代器:input_iterator
只写迭代器:output_iterator
单向迭代器:forward_iterator只能++操作
双向迭代器: bidirectional_iterator支持++--操作
随机迭代器:randomAccess_iterator支持++--+=-=、等操作, 访问任意位置

继承关系:

【C++】vector类模拟实现_第4张图片
接下来就可以写现代写法的拷贝构造了:

		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endofstorage, v._endofstorage);
		}
		
		// 现代写法
		// v1(v2)
		vector(const vector<T>& x)
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			vector<T> tmp(x.begin(), x.end());
			swap(tmp);
		}

2.10 赋值拷贝

		vector<T>& operator=(const vector<T>& x)
		{
			for (size_t i = 0; i < x.size(); i++)
			{
				push_back(x[i]);
			}
			return *this;
		}

赋值拷贝跟拷贝构造的区别是:

赋值拷贝是把一个类赋值给另一个已经构造好的类,而拷贝构造是创建出一个新的类并赋值。

2.10.1 现代写法

		//现代写法
		vector<T>& operator=(vector<T> x)
		{
			swap(x);
			return *this;
		}

2.11 插入

		iterator insert(iterator pos, const T& val)
		{
			assert(pos <= _finish);
			assert(pos >= _start);
			if (size() == capacity())
			{
				// 扩容
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : 2 * capacity());
				pos = _start + len;
			}
			// 移动数据
			iterator cur = end() - 1;
			while (cur >= pos)
			{
				*(cur + 1) = *cur;
				cur--;
			}
			*pos = val;
			_finish++;
			return pos;
		}

为什么要用迭代器当返回值?

插入可能改变空间,导致迭代器失效。

2.12 删除

		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			assert(size() > 0);
			iterator del = pos;
			while (del != end())
			{
				*del = *(del + 1);
				del++;
			}
			_finish--;
			return pos;
		}

为什么要用迭代器当返回值?

有的删除可能会缩容,导致迭代器失效。

你可能感兴趣的:(C++,c++,算法)