十二、模拟实现 vector

Ⅰ . 实现基本框架

01 结构的定义

成员变量的定义:

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

namespace yxt
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
	private:
		iterator _start;	// 开始位置
		iterator _finish;	// 结束位置
		iterator _end_of_storage;
	};
}

我们发现,成员函数和之前模拟实现的 string 的写法不一样了,但虽然表面上看起来不一样,但实际表达的效果是大同小异的:

我们用指针记录 _start,_finish,_end_of_storage 的位置,只需要指针减指针就可以得到大小和容量

再说回代码的实现,为了和库中的 vector 进行区分,我们这里依然用命名空间包含起来。

我们这里造一个 vector 的类模板去适应各种类型,我们用 typedef 将 T* 重命名为 iterator。

02 构造函数的实现

代码实现:

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

03 析构函数的实现

代码实现:

		/* 析构函数 */
		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _end_of_storage = nullptr;
			}
		}

04 实现 size() 和 capacity() 

代码实现:

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

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

05 实现 push_back() 尾插

我们先实现一个简单的 push_back() ,这里使用 new 来实现

检查是否需要增容

需要增容,就先增容后再插入数据;不需要增容,就直接插入数据。

我们先去思考,如何判断是否需要增容 ——

我们之前的判断方式是 size == capacity 的时候需要增容

问题是,这次我们没有定义 _size 和 _capacity

当 _finish 触及到 _end_of_storage 时,不就说明容量不够了吗?

增容的步骤

我们再来思考一下增容部分的实现,我们在 vector 常用接口介绍章节里说过:

" vector 使用动态分配数组来存储它的元素。当新元素插入时,为了增加存储空间,这个数组就需要被重新分配大小。具体做法是分配一个新的数组,然后将全部元素转移到这个新的数组。"

因此,我们的增容操作可以大致可分为4个步骤:

① 开一块带有新容量的空间存到 tmp 中。

② 再把原空间的数据拷贝到新空间。

③ 并释放原有的旧空间

④ 最后将 _start、_finish 和 _end_of_storage  指向新的空间。

注意:

① 最后一步如果先将 _start 指向 tmp 后,再计算 _finish 时,此时不能现场算 size() ,现场算会出问题,因为 _start 已经被更新成 tmp 了

② 如果不想改变顺序,还是想按 _start、_finish 和 _end_of_storage 的顺序赋值,我们可以提前把 size() 算好,存到一个变量中。

此外,真 vector 这里扩容是要调空间配置器的,开空间和初始化是分开的。

我们这里的实现也没有空间配置器

对于空间配置器的知识我们放到后面再说,我们目前的重点不是空间配置器,重点是 vector。

至于新容量给多少,我们还是按照自己的习惯,首次给 4 默认扩 2 倍的方式去增容。

插入数据

检查增容和增容都已经分析完了,最后就只剩一个插入了,插入是最简单的:

代码实现:

		/* 尾插 */
		void push_back(const T& x)
		{
			// 检查是否需要扩容
			if (_finish == _end_of_storage)
			{
				size_t new_capacity = capacity() == 0 ? 4 : capacity() * 2;
				size_t sz = size();

				T* tmp = new T[new_capacity];
				if (_start)
				{
					memcpy(tmp, _start, size() * sizeof(T));
					delete[] _start;
				}

				_start = tmp;
				_finish = _start + sz;
				_end_of_storage = _start + new_capacity;
			}
			// 插入数据
			*_finish = x;
			_finish++;
		}

接下来,我们实现一下 operator[] ,利用下标 + 方括号的方式来遍历一下

06 实现 operator[]

T:由于我们不知道返回值类型,所以给 T。

T&:引用返回减少拷贝。

const:这里 cosnt 修饰 T 和 this,是为了限制写。

代码实现:

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

测试 push_back:

	void vector_test1()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);

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

运行结果如下:

Ⅱ . 迭代器的实现

01 实现迭代器的 begin 和 end

vector 的迭代器是一个原生指针:

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

begin() :

		/* begin() */
		iterator begin()
		{
			return _start;
		}

end() :

		/* end() */
		iterator end()
		{
			return _finish;
		}

02 实现 const 迭代器的 begin 和 end

const 类型的迭代器,即可读不可写。在实现的时候用 const 修饰即可:

begin():

		/* const begin() */
		const_iterator begin() const
		{
			return _start;
		}

end():

		/* const end() */
		const_iterator end() const
		{
			return _finish;
		}

03 测试

测试迭代器的效果:

	void vector_test1()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);

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

		// 迭代器
		vector<int>::iterator it = v.begin();
		while (it != v.end())
		{
			cout << *it << " ";
			it++;
		}
		cout << endl;
	}

运行结果如下:

我们知道,实现了迭代器 (识别 begin, end) ,范围 for 也就能用了,我们来试试:

	void vector_test1()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);

		// 下标 + []
		for (size_t i = 0; i < v.size(); i++)
		{
			cout << v[i] << " ";
		}
		cout << endl;

		// 迭代器
		vector<int>::iterator it = v.begin();
		while (it != v.end())
		{
			cout << *it << " ";
			it++;
		}
		cout << endl;

		// 范围 for
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

运行结果如下:

迭代器的写:

	void vector_test2()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);

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

		// 迭代器
		vector<int>::iterator it = v.begin();
		while (it != v.end())
		{
			*it *= 2;
			cout << *it << " ";
			it++;
		}
		cout << endl;
	}

运行结果如下:

测试一下 const 迭代器:

	void vector_test3()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);

		// const 迭代器
		vector<int>::const_iterator it = v.begin();
		while (it != v.end())
		{
			cout << *it << " ";
			it++;
		}
		cout << endl;
	}

运行结果如下:

如果对 const 迭代器进行 "写" 操作:

会报错 error C3892: “it”: 不能给常量赋值

04 实现迭代器区间

我们在构造时需要注意,使用迭代器区间必须是左闭右开

(一个类模板的成员函数,又可以是一个函数模板)

迭代器区间:

		/* 迭代器区间 */
		template<class InputInterator>
		vector(InputInterator first, InputInterator last)
		{
			while (first != last)
			{
				// 逐个插入
				push_back(*first);
				++first;
			}
		}

	private:
		iterator _start = nullptr;	// 开始位置
		iterator _finish = nullptr;	// 结束位置
		iterator _end_of_storage = nullptr;

现在我们就可以用迭代器区间去初始化 vector 了

为什么这里要叫 InputIterator?不用它行不行?

05 迭代器的分类

就功能上来说:D > C > B > A 

它们本质上是一个继承关系:下面是子类,上面是父类。

子类都是一个父类,因为它满足父类的所有特征。

也就是说,虽然在语法上它是个模板,允许你传任意类型的迭代器,

但是在更深层次上存在着更进一步的限制 ——

① 它要求你传随机迭代器,你就不能用双向迭代器。因为只有随机迭代器才能满足随机迭代器的所有操作。换言之,你不能用功能比它指定的迭代器少的迭代器。(可以理解为权限的放大)

② 它要求你用双向迭代器,你就不能用单向迭代器,因为单项迭代器不能满足所有双向迭代器的操作。但是你可以用比它功能多的迭代器,比如随机迭代器,因为随机迭代器也能满足双向迭代器的操作。因为随机迭代器是双向迭代器的子类,它满足父类(双向迭代器)的所有功能。(可以理解为权限的缩小)

我们弄明白了这些,我们再回到刚才提的问题 ——

为什么这里要叫 InputIterator ?不用它行不行?

首先,InputIterator 是输入迭代器,这么写是为了满足命名规范。

可以不用,我们可以传单向迭代器、双向迭代器,也可以传随机迭代器。

因为这些迭代器都满足输入迭代器的所有功能。

Ⅲ . vector 的扩容

01 reserve

我们要实现 vector 的 insert,肯定需要用到增容

我们可以把刚才写 push_back 实现的增容部分拎出来,实现一个 CheckCapacity 函数。

但是我们这里可以直接实现出 reserve,到时候实现 resize 也可以复用得上,岂不美哉?

所以,我们先实现 reserve

02 实现 reserve 

首先还是检查是否真的需要扩容,如果传入的 new_capacity 确实比现有 capacity 要大,

并且 _finish == _eos 时,我们就进行扩容,这相当于是一个检测,

防止根本就不需要扩容,还给它扩容的情况。还是分为四个步骤:

① 开新空间:new 空间的地方,我们直接给上传入的 new_capacity 即可,要求开多少就开多少。

② 拷贝数据:"暂时先用" memcpy(如果你知道 memcpy 的问题,先别急,我们最后再专门探讨)

③ 释放旧空间:释放 _start,new[] 对应 delete[] 去释放。

④ 指向新空间:把 size() 提前算好,然后让 _start、_finish 和 _eos 指向新空间。

代码实现:

		/* reserve */
		void reserve(size_t new_capacity)
		{
			if (new_capacity > capacity())
			{
				if (_finish == _end_of_storage)
				{
					size_t old_size = size();
					T* tmp = new T[new_capacity];

					if (_start)
					{
						memcpy(tmp, _start, sizeof(T) * size();
						delete[] _start;
					}

					// 指向新空间
					_start = tmp;
					_finish = _start + old_size;
					_end_of_storage = _start + new_capacity;
				}
			}
		}

03 push_back 复用 reserve

实现完 reserve 之后,我们可以把刚才的 push_back 简化一下:

还是按首次给4,默认扩2倍的形式走

三目运算符得到的结果传入 reserve,结果变成 reserve 中的 new_capacity 参数,

然后 reserve 执行 new 的时候,会按照传入的 new_capactiy 的大小去开空间。

代码实现:

		/* 尾插 */
		void push_back(const T& x)
		{
			// 检查是否需要扩容
			if (_finish == _end_of_storage)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}
			// 插入数据
			*_finish = x;
			_finish++;
		}

04 使用 memcpy 拷贝问题

我们一开始实现的 push_back 就用了 memcpy 进行拷贝的,

然后我们刚才实现了 reserve,因而又让 push_back 复用了 reserve, 

reserve 搬元素的时候也是 memcpy 去进行拷贝的,其实这里存在一个非常严重的问题!

我们现在给出一个测试用例:

	void vector_test4() 
	{
		vector<string> v;      // 在vector里放string
		v.push_back("233333333333333333");
		v.push_back("233333333333333333");
		v.push_back("233333333333333333");
		v.push_back("233333333333333333");
		v.push_back("233333333333333333");
		v.push_back("233333333333333333");
		v.push_back("233333333333333333");

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

运行结果如下:

为什么会这样?原因在于我们在扩容和深拷贝时,用了一个 memcpy

push_back 调用 reserve 扩容时就会出问题,根本原因是 memcpy 是浅拷贝

此时,再让 _start = _tmp 时,指向的就是野指针

分析:memcpy 是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中。如果拷贝的是自定义类型的元素,memcpy 既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为 memcpy 的拷贝实际是浅拷贝。

解决方法:手动去拷贝,不使用 memcpy

		/* reserve */
		void reserve(size_t new_capacity)
		{
			if (new_capacity > capacity())
			{
				if (_finish == _end_of_storage)
				{
					size_t old_size = size();
					T* tmp = new T[new_capacity];

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

					// 指向新空间
					_start = tmp;
					_finish = _start + old_size;
					_end_of_storage = _start + new_capacity;
				}
			}
		}

如果 T 是 int,一个一个拷贝没问题,
如果 T 是 string 等自定义问题,一个一个拷贝调用的是 T 的深拷贝,也不会出问题。

我们拿刚才的测试用例测试一下,看看效果如何:

05 实现 resize()

resize 就是带初始化的 reserve()

代码实现:

		/* resize */
		void resize(size_t new_capacity, const T& val = T())
		{
			// 容量足够
			if (new_capacity < size())
			{
				_finish = _start + new_capacity;
			}
			else
			{
				// 容量不够,检查是否需要扩容
				if (new_capacity > capacity())
				{
					reserve(new_capacity);
				}

				while (_finish != _start + new_capacity)
				{   // 初始化
					*_finish = val;  // 按val初始化,默认缺省为 T()
					_finish++;
				}
			}
		}

vector 的 resize 如果不给第二个参数,默认给的是其对应类型的缺省值作为填充值

由于我们不知道具体类型是什么,这里缺省值用匿名对象

此外因为匿名对象的生命周期只在当前一行,这里必须用 const 引用匿名对象

06 实现 pop_back

pop_back 很简单,只需要 _finish-- 就可以了。

 但是需要考虑删完的情况,我们这里采用暴力的处理方式 —— 断言。

代码实现:

		/* 尾删 */
		void pop_back()
		{
			assert(_finish > _start);
			_finish--;
		}

测试一下:

	void vector_test5()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);

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

		v.pop_back();

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

运行结果如下:

Ⅳ . 迭代器失效问题

01 通过 insert / erase 了解迭代器失效问题

什么是迭代器失效?

"迭代器失效是一种现象,由特定操作引发,这些特定操作对容器进行操作,使得迭代器不指向容器内的任何元素,或者使得迭代器指向的容器元素发生了改变。"

迭代器的主要作用是让算法可以不关心底层的数据结构,其底层实际就是一个指针

或者是对指针进行了封装

因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了

而使用一块已经被释放的空间,造成的后果是程序崩溃

02 实现 insert

插入可分为四个步骤:① 检查 pos 是否越界 ② 检查是否需要扩容 ③ 移动数据 ④ 插入数据

代码实现:

			/* 插入 */
		void insert(iterator pos, const T& x)
		{
			assert(pos >= _start);
			assert(pos <= _finish);

			// 检查是否需要扩容
			if (_finish == _end_of_storage)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}

			// 挪动数据
			iterator  end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}

			// 插入数据
			*pos = x;
			_finish++;
		}

测试:

void vector_test6()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	vector<int>::iterator pos = find(v1.begin(), v1.end(), 2);
	if (pos != v1.end()) 
	{
		v1.insert(pos, 20);
	}

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

运行结果如下:

看起来似乎没有问题,我们再插入一个数据看看:

	void vector_test6()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		vector<int>::iterator pos = find(v1.begin(), v1.end(), 2);
		if (pos != v1.end()) 
		{
			v1.insert(pos, 20);
		}

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

运行结果如下:

迭代器失效问题。扩容导致的 pos 失效,我们的 insert 没有去处理这个问题。

如果发生扩容,我们的 pos 是不是应该去更新一下

代码实现:

		/* 插入 */
		void insert(iterator pos, const T& x)
		{
			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 = x;
			_finish++;
		}

运行结果如下:

但是外面的 pos(实参) 还是失效的,这里是传值,pos(形参) 是 pos(实参) 的临时拷贝

如果 insert 中发生了扩容,那么会导致 pos(实参)指向空间被释放。

pos(实参) 本身就是一个野指针,这种问题我们称之为 —— 迭代器失效 

如何解决这里的迭代器失效问题?传引用?

传引用当然时不好的,有的 vector 还会缩容呢,传引用不能彻底解决所有问题

我们来看看大佬是如何解决这一问题的:

它们是通过返回值去拿的,返回新插入的迭代器。

如果迭代器失效了,你想拿另一个迭代器去代替,就可以通过返回值去拿一下

代码实现:

		/* 插入 */
		iterator insert(iterator pos, const T& x)
		{
			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 = x;
			_finish++;

			return pos;
		}

03 实现 erase

代码实现:

		/* 删除 */
		void erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos <= _finish);

			iterator begin = pos + 1;
			while (begin < _finish)
			{
				*(begin - 1) = *begin;
				begin++;
			}
			_finish--;
		}

测试:

	void vector_test7()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		vector<int>::iterator pos = find(v1.begin(), v1.end(), 2);
		if (pos != v1.end())
		{
			v1.erase(pos);
		}

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

运行结果如下:

那 erase 有没有迭代器失效的问题呢?

比如我们要求删除所有偶数:

	void vector_test7() 
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(5);

		// 要求删除v1所有的偶数
		vector<int>::iterator pos = find(v1.begin(), v1.end(), 2);
		while (pos != v1.end()) 
		{
			if (*pos % 2 == 0) 
			{
				v1.erase(pos);
			}
			pos++;
		}

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

我们分三种情景去测试:

1 2 3 4 5
1 2 4 5
1 2 3 4

测试结果如下:

① 1 2 3 4 5 正常

② 1 2 3 4 崩溃

③ 1 2 4 5 结果不对

对于情况 ③ :比如连续的偶数,导致后一个偶数没有判断,导致没有删掉。

再其次,erase 的删除有些 vector 版本的实现,不排除它会缩容。

如果是这样,erase(pos) 以后,pos 也可能会是野指针,跟 insert 类似。

对于情况 ② :如果最后一个数据是偶数,会导致 erase 以后,pos 意义变了。

再 ++ 一下,导致 pos 和 end 错过结束判断,出现越界问题。

而情况 ① :之所以没有翻车,是因为被删除的偶数后面恰巧跟的是奇数

导致上述三种问题的本质:erase(pos) 以后,pos 的意义变了,再去 pos++ 是不对的。

为了解决这个问题,erase 是这么说明的:

改进代码实现:

		/* 删除 */
		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos <= _finish);

			iterator begin = pos + 1;
			while (begin < _finish)
			{
				*(begin - 1) = *begin;
				begin++;
			}
			_finish--;

			return pos;
		}

测试:

	void vector_test7() 
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(4);
		v1.push_back(5);

		// 要求删除v1所有的偶数
		vector<int>::iterator pos = find(v1.begin(), v1.end(), 2);
		while (pos != v1.end()) 
		{
			if (*pos % 2 == 0) 
			{
				pos = v1.erase(pos);
			}
			else 
			{ 
				pos++; 
			}
		}

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

运行结果如下:

04 可能导致迭代器失效的操作

对于 vector 可能会导致其迭代器失效的操作有:

① 会引起其底层空间改变的操作,都有可能存在迭代器失效。

比如:resize、reverse、insert、assign、push_back 等。

② 指定位置元素的删除操作:erase

int main()
{
	int a[] = { 1, 2, 3, 4 };
	vector<int> v(a, a + sizeof(a) / sizeof(int));
	// 使用find查找3所在位置的iterator
	vector<int>::iterator pos = find(v.begin(), v.end(), 3);
	// 删除pos位置的数据,导致pos迭代器失效。
	v.erase(pos);
	cout << *pos << endl; // 此处会导致非法访问
	return 0;
}
 
 

erase 删除 pos 位置元素后,pos 位置之后的元素就会往前搬移,

没有导致底层空间的改变,理论上讲迭代器不应该会失效。

但是 pos 刚好是最后一个元素,删完之后 pos 刚好在 end 的位置,

而 end 位置是没有元素的,那么 pos 就失效了。

因此删除 vector 中任意位置元素时,VS 就认为该位置迭代器失效了。

还有就是我们刚才讲解的奇偶数,删除 pos 位置的数据,导致 pos 迭代器失效。

当然,vector 迭代器的失效主要发生在 insert 和 erase。vector 的其他接口基本不碰迭代器,自然也就不涉及这些问题。

迭代器失效解决方法:在使用前,对迭代器重新赋值即可。

string 的 insert 和 erase 迭代器是否会失效?string 有没有迭代器失效?

当然会,只要使用迭代器的容器,都可能会涉及迭代器失效。

只是 string 一般很少涉及迭代器失效,因为它 insert 和 erase 时主要用下标。

Ⅴ . 深拷贝

01 拷贝构造

可以使用传统写法,也可以使用现代写法。

传统写法:

/* v2(v1) */
vector(const vector<T>& v) {
	//_start = new T[v.capacity()];
	//_finish = _start + v.size();
	//_end_of_storage = _start + v.capacity();
 
	reserve(v.capacity());    
	// memcpy(_start, v._start, v.size() * sizeof(T));  // 会翻车
	for (const auto& e : v) 
    {
		push_back(e);
	}
}

注意这里不能使用 memcpy,这个我们前面已经强调过了。

现代写法:

		/* 拷贝构造 */
		vector(const vector<T>& v)
		{
			reserve(v.size());
			for (auto& v : v)
			{
				push_back(v);
			}
		}

测试:

	void vector_test8() 
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);

		vector<int> v2(v1);
		for (auto e : v2) 
			cout << e << " "; 
		cout << endl;
	}

运行结果如下:

02 赋值构造

这里我们直接用现代写法

代码实现:

		/* 赋值构造 */
		vector<T>& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}

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

测试:

	void vector_test9() 
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		cout << "赋值前:";
		for (auto e : v1) 
			cout << e << " "; 
		cout << endl;

		vector<int> v3;
		v3.push_back(10);
		v3.push_back(20);
		v3.push_back(30);

		cout << "赋值后:";
		v1 = v3;
		for (auto e : v1) 
			cout << e << " "; 
		cout << endl;
	}

运行结果如下:

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值