c++手撕维克托:加入光明的进化吧

如标题,给这篇文章来一个非常中二的配图,致敬在码路上不断进化的我们

一. 标准库中维克托的各种小技巧

(1).维克托一共有很多种初始化

空初始化vector<int> vec;创建空容器,后续动态添加元素
指定大小vector<int> vec(5);初始化为指定大小,元素默认值为 0
指定大小和值vector<int> vec(5, 10);初始化为指定大小,所有元素为指定值
列表初始化vector<int> vec = {1,2,3};C++11 起支持,直观指定初始元素
数组初始化vector<int> vec(arr, arr+5);从数组指定范围复制元素
复制初始化vector<int> vec2(vec1);创建已有 vector 的副本
迭代器范围初始化vector<int> vec(begin, end);从其他容器的迭代器范围复制元素
移动初始化vector<int> vec(createVector());C++11 起支持,通过移动资源避免拷贝
二维 vector 初始化vector<vector<int>> m(3,4,0);创建多维数组,常用于矩阵等场景
assign 方法初始化vec.assign(5, 10);先创建空 vector,再通过 assign 赋值

(2)维克托 中operator[]和at的比较

operator []允许我们像访问数组一样访问维克托,但也缺少对边界条件的检测

vertor::at会检测是否越界,如果越界会抛出异常

int main()
{
	vector<int> v(5, 10);
	int b=v.at(5);
}

相对来说,at是比operator[ ]更高效的访问方式

二.对vector增删查改的手撕实现

namespace theshy
{
	template<class T>
	class vector
	{
		typedef T* iterator;
		typedef  const T* const_iterator;
	public:
		
		iterator begin()
		{
			return arr;
		}
		iterator end()
		{
			return arr + _size;
		}
		const_iterator begin()const
		{
			return arr;
		}
		const_iterator end()const
		{
			return arr + _size;
		}

		vector(int size = 0)
			:_capacity(size)
		{
			_size = 0;
			arr = new T[size];
			cout << "构造函数" << endl;
		}
		vector(const vector<T>& a)
		{
			_size = a._size;
			_capacity = a._capacity;
			arr = new T[_size];
			for (int i = 0; i < _size; i++)
			{
				arr[i] = a.arr[i];
			}
			cout << "拷贝构造" << endl;
		}
		~vector()
		{

			delete[] arr;
			arr = nullptr;
			_size = 0;
			_capacity = 0;
			cout << "析构函数" << endl;
		}
		vector<T>& operator=(const vector<T>& a)
		{
			delete[] arr;
			arr = nullptr;
			_size = 0;
			_capacity = 0;
			_size = a._size;
			_capacity = a._capacity;
			arr = new T[_size];
			for (int i = 0; i < _size; i++)
			{
				arr[i] = a.arr[i];
			}
			cout << "赋值重载函数" << endl;
		 }
		void swap(vector<T>& v)
		{
			std::swap(v.arr, arr);
			std::swap(v._size, _size);
			std::swap(v._capacity, _capacity);
		}
		void pushback(const T& a )
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
				
			}
			arr[_size] = a;
				_size++;
		}
		void insert(int pos, int n, const T& a)//在指定位置之前插入数据,分三步,一步是开空间,一步是移动数据,一步是插入数据
			//移动时避免覆盖从后往前移动
			///数组的分块[pos,pos+n-1]插入,[pos+n,size+n-1]移动区
		{
			reserve(_size + n );
			for (int i = _size -1; i >= pos; i--)
			{
				arr[i + n] = arr[i];
			}
			for (int i = pos; i <= pos + n - 1; i++)
			{
				arr[i] = a;
			}
			_size += n;
		}
		void insert(int pos, const T& a)
			//插入a
		{
			reserve(_size + 1);
			for (int i = _size - 1; i >= pos; i--)
			{
				arr[i +1] = arr[i];
			}
				arr[pos] = a;
			
			_size += 1;
		}
		void popback()//尾删
		{
			assert(_size != 0);
			_size--;
		}
		void erase(int pos)
		{
			
			for (int i = pos + 1; i <= _size - 1; i++)
			{
				arr[i - 1] = arr[i];
				}
			_size--;
			

		}//数组分块,[pos]为删除数据,[pos+1,size-1]向前移移动一位
		void reserve(int b )
		{
			if (b > _capacity) {
				//扩容
				vector<T> a(b);
				a._size = _size;
				for (int i = 0; i < _size; i++)
				{
					a.arr[i] = arr[i];
				}
				a.swap(*this);
				cout << "capacity make a change for:" << _capacity << endl;

			}
			return;
		}// 这里尝试用智能指针的思路去优化代码行数,但是发现了一个问题void reserve(int a )
		//{
		//	if (a > _capacity) {
		//		//扩容
		//		vector<T> a(a);
		//		a._size = _size;
		//		for (int i = 0; i < _size; i++)
		//		{
		//			a.arr[i] = arr[i];
		//		}
		//		a.swap(*this);
		//		cout << "capacity make a change for:" << _capacity << endl;

		//	}
		//	return;
		//}这里发现当使用reserve的时候capacity一直不发生变化,究其原因是因为vector<T> a(a) 和形参a重名了
		//导致匹配上了拷贝构造函数,一直给自己赋值,这提醒我必须重视命名
		T& operator[](int pos)
		{
			assert(pos < _size);
			return arr[pos];
		}
		int size()
		{
			return _size;
		}
		int capacity()
		{
			return _capacity;
		}//注意这里不能加引用,因为随便修改成员是非常危险的行为
		void print()
		{
			T* p = arr;
			for (int i = 0; i < _size; i++)
			{
				cout << *p << endl;
				p++;
			}
			cout << "size=" << _size << endl;
			cout << "capacity=" << _capacity << endl;
		}
	protected:
		T* arr;
		int _size;
		int _capacity;

	};
}
int main()
{
	vector<int> v(5, 10);
	int b=v.at(5);
}
/*
比尔盖茨曾说过:“用代码行数来衡量程序的开发进度, 就好比用重量来衡量飞机的制造进度。”*/

比上次string手撕进步的点是,更加熟练了。提高了对模板的运用能力 。在对数组分块的问题上吸取了上次逻辑不明的教训,这次先理顺逻辑再写代码

不足是在命名问题上还是太粗心了,下次得把命名规范起来,像标准库靠进

三.迭代器失效问题

会引起其底层空间改变的操作,都有可能是迭代器失效 ,比如: resize reserve insert
assign push_back 等。
一下列出两种迭代器失效的情况

情况一

 

int main()
{
	vector<int> v(5, 10);
	auto it = v.begin();
	v.resize(100, 8);
	while (it != v.end())
	{
		cout << *it << endl;
		++it;
	}
	return 0;
}

 这里会发生迭代器失效的情况,因为resize之后之前it指向的空间就失效了。

情况二

int main()
{
	int a[] = { 1,1,5,5,5,5,5,5,5,5,5,5,5,7 };
	vector<int> v(a,a+14);
	auto it = find(v.begin(), v.end(), 7);
	v.erase(it);
	cout << *it << endl;
	return 0;
}

这里也会发生迭代器失效的情况。因为it指向的是被删除的位置。一般是没有问题的,但是如果这恰好是数组中最后一个位置的话就会发生越界的情况。

解决迭代器失效的办法:重新给迭代器赋值一下就好了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值