【C++】手把手教你写出自己的vector类

在上一篇博客中,我们学习了vector 的基本使用,以及 迭代器的失效问题:

【C++】深入理解vector类(一)

今天我们来模拟实现以下vector类。

目录

    • 成员变量
    • 接口实现
      • 构造函数
      • 迭代器
      • 拷贝构造
      • 赋值
      • reserve
      • resize
      • push_back
      • pop_back
      • 实现[ ]访问

成员变量

我们先从原码中找出其成员变量:
【C++】手把手教你写出自己的vector类_第1张图片
可以看到 ,原码中有三个成员变量:

  • start
  • finish
  • end_of_storage

数据类型是 iterator


很显然,这和 string 类中的类似,分别对应 start size capacity

【C++】手把手教你写出自己的vector类_第2张图片
那么iterator 是如何声明的?
【C++】手把手教你写出自己的vector类_第3张图片

所以iterator 是一个模板类型。可以根据用户的输入 而 变成相应的数据类型。


所以,vector 的基本框架这样写:

namespace yyk
{
 typedef T*  iterator;
 typedef const T* const_iterator;

 class vector{
   public:
  
   private:
     iterator _start;
     iterator _finish;
     iterator _endofstorage;
};
  
}

接口实现

构造函数

  1. 无参构造
vector()
	:_start(nullptr),
	_finish(nullptr),
	_endofstorage(nullptr)
	{}

  1. 迭代器构造

在上一篇文章中,我们在介绍构造函数的时候,提到了迭代器构造函数。
简单来说,就是我们可以用其他的容器的迭代器来初始化自己。

vector (Inputlterator first, Inputiterator last)

使用范例:

int myints[] = {16,2,77,29};
std::vector<int> fifth (myints, myints + 4 );


std::vector<int> second (4,100);                      
std::vector<int> third (second.begin(),second.end());

这里的类模板的成员函数,为什么还要定义一个模板参数 InputIterator?

为什么不直接使用iterator呢?

很显然,如果我们写出iterator ,那么我们只能使用vector的迭代器去初始化。如果想使用其他容器,比如list,string中的元素来初始化我们的vector容器,那么就不行了。

比如:

  string s("abcde");
  vector<int>v4(s.begin(),s.end());

模拟实现:

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

迭代器

对于迭代器我设置两种,一种是可读可写的,一种是只可读的

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 _endofstorage - _start;
		}


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

		const T& operator[] (size_t n)const

		{
			assert(n < size());
			return  *(_start + n);
		}

拷贝构造

与之前模拟string 类的时候相同,对于拷贝构造,我们有不止一种的写法。

  • 传统写法1

很显然,我们需要使用深拷贝,因为浅拷贝有两个问题:

  1. 一段空间会析构两次
  2. 拷贝出的vector容器改变会导致原vector容器也发生改变

同时为了避免传值时候的深拷贝,我们使用引用传值。没有印象的同学可以去看看以下string类的模拟实现博客。

vector(cosnt vector<T>& v)
{
  _start =new T[v.capacity()];
  memcpy(_start,v.start,sizeof(T)*v.size());
  
  _finish=_start+v.size();
  _endofststorage=_start+v.capacity();
]
  • 传统写法2

这种写法与上一种大同小异,也是先开辟新空间,再将v中的元素逐个插入到这篇空间里

vector(const vector<T>& v)
   :_start(nullptr),
   _finish(nullptr),
   _endofstorage(nullptr)
{
  reserve(v.capacity);
  for(const auto& e: v)
  {
     push_back(e);
  }
}
  • 现代写法

这个写法与string类的现代写法,也就是“坐享其成”。这里不做过多讲解。

vector(const vector<T>& v)
	:_start(nullptr),
	 _finish(nullptr), 
	 _endofstorage(nullptr)
		{
			vector<T> tmp (v.begin(), v.end());
			swap(tmp);
		}

但是,由于连续空间的深拷贝并不复杂,这里的现代写法相较于传统写法的实际优势并不大。

赋值

对于赋值,我们也有传统写法和现代写法。

  • 传统写法

传统写法 是先把待赋值对象销毁,再重新开空间,然后向这块空间赋值。

vector<T>& operator=(const vector<T>& v)
		{
			if (this != &v)
			{
				delete[]_start;
				_start = _finish = _endofstorage = nullptr;

				reserve(v.capacity());
				for (cosnt auto& e : v)
				{
					push_back(e);
				}
			}

			return *this;
		}

-现代写法

这个在string类的模拟中也写过了,不再赘述。

vector<T>& operator=(const vector<T>& v)
		{
			vector<T> tmp(v.begin(), v.end());
			swap(tmp);

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

reserve

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

				_start = tmp;
				_finish = tmp + sz;
				_endofstorage = tmp + n;
			}
		}

resize

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

push_back

void push_back(const size_t x)
		{
			
			if (_start == _endofstorage)
			{
				int newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);
			}

			*_finish = x;
			_finish++;

		}
		void pop_back()
		{
			assert(size()!=0);

			--_finish;
		
		}

pop_back

void pop_back()
		{
			assert(size()!=0);

			--_finish;
		
		}

实现[ ]访问

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

const T& operator[] (size_t n)const

		{
			assert(n < size());
			return  *(_start + n);
		}

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