2024年最新C++入门 vector的使用 + 进阶【模拟实现】_c(1),2024年最新字节跳动面试官

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

在linux上出现段错误
在这里插入图片描述
图解析:
在这里插入图片描述
从图中我们可以看出it每次删元素的时候都会跳过一个元素,end的位置每次也都在发生变化,直到删除6的时候 it 和 end已经开始错开了,分道扬镳,此后再也不会相遇,it此时就是野指针了,访问了一块非法的内存,但是在linux上平台上我们只需要将尾插一个7(奇数)就能解决这个问题,vs下无论是最后一个值是奇数还是偶数都会报错,it已经失效,编译器会直接对it++检查
在这里插入图片描述
是因为当插入7的时候刚好检查到最后一个位置不会再erase掉,it再往后迭代的时候就遇到了end,循环也就终止了,但是这段代码还是错误的代码,因为不同的平台跑出的结果是不一样的

解决方案

vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (\*it % 2 == 0)
		{
			//erase删除it位置的元素后,会返回it位置的下一个位置,
			//再回去检查这个元素是否是偶数
			it = v.erase(it);
		}
		else 
		{
			it++;
		}	
	}

总结:
insert和erase都会导致迭代器失效
1、insert(it, x) 或者 erase(it) 以后迭代器的意义变了
2、insert(it, x) 或者 erase(it) 以后it变成了野指针

vector模拟实现

namespace mzt 
{
	template<class T>
	class vector
	{
	public:
		typedef T\* iterator;
		iterator begin() { return _start; }
		iterator end() { return _finish; }
		size_t capacity() { return _endofstroage - _start; }
		size_t size() { return _finish - _start; }

		vector()
			:\_start(nullptr)
			, \_finish(nullptr)
			, \_endofstroage(nullptr)
		{ }
		vector(vector<T>& v)
			:\_start(nullptr)
			,\_finish(nullptr)
			,\_endofstroage(nullptr)
		{
			reserve(v.capacity());
			for (auto &e : v) 
			{
				push\_back(e);
			}
		}

		void swap(vector<T>& v)
		{
			::swap(_start,v._start);
			::swap(_finish,v._finish);
			::swap(_endofstroage,v._endofstroage);
		}
		vector<T>& operator=(vector<T> v)
		{
			if (this != &v) 
			{
				swap(v);
			}
			return \*this;
		}

		void reserve(size_t n) 
		{
			size_t sz = size();
			T\* tmp = new T[n];
			if (n > capacity())
			{
				//memcpy(tmp,\_start,sizeof(T) \* n);
				if (_start)
				{
					for (size_t i = 0; i < sz; i++)
					{
						tmp[i] = _start[i];
					}
					delete[]_start;
				}

				_start = tmp; 
				_finish = _start + sz;
				_endofstroage = _start + n;
			}
		}
		//尾插
		void push\_back(const T& v)
		{
			if (_finish == _endofstroage)
			{
				size_t newcapacity = capacity() == 0 ? 4 : capacity() \* 2;
				reserve(newcapacity);
			}
			\*_finish++ = v;
		}
		void erase() 
		{
			assert(!empty());
			--_finish;
		}

		void resize(size_t n,T a = T()) 
		{
			if (n < capacity()) 
			{
				_finish = _start + n;
			}
			else 
			{
				if (n > capacity()) 
				{
					reserve(n);
				}
				while (_finish < _endofstroage) 
				{
					\*_finish++ = a;
				}
			}
		}
		size_t operator[](size_t n) 
		{
			assert(n < size());
			return _start[n];
		}
	
		iterator insert(iterator pos, const T& x)
		{
			if (_finish == _endofstroage) 
			{
				size_t len = pos - _start;
				size_t newcapacity = capacity() == 0 ? 4 : capacity() \* 2;
				reserve(newcapacity);
				//pos失效后重新计算pos的位置
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos) 
			{
				\*(end + 1) = \*end;
				end--;
			}	
			\*pos = x;
			++_finish;
			return pos;
		}
		iterator erase(iterator pos)
		{
			assert(!empty());
			iterator it = pos + 1;
			while (it != _finish) 
			{
				\*(it - 1) = \*it;
				it++;
			}
			--_finish;
			return pos;
		}

		bool empty() { return _start == _finish; }

		~vector() 
		{
			if (_start) 
			{
				delete[] _start;
			}
			_start = _finish = _endofstroage = __nullptr;
		}

	private:
		iterator _start;
		iterator _finish;
		iterator _endofstroage;
	};


}

私有成员

iterator _start;//返回起始位置
iterator _finish;//返回最后一个元素的下一个位置
iterator _endofstroage;//返回空间的结束标记位置

reserve

//返回最后一个元素的下一个位置
size_t size()const
{
	return _finish - _start;//指针相减计算中间差的元素个数
}

void reserve(size_t n) 
{
	if(n > capapcity())
	{
		size_t sz = size();
		//提前备份好sz大小,防止扩容后\_start 指向了新的空间,
		T\* tmp = new T[n];
		if (_start) 
		{
			//旧的空间\_start的值拷贝到新空间上tmp上
			//memcpy(tmp, \_start, sizeof(T) \* n);
			//memcpy针对内置类型是可以的,但是如果针对
			//的是自定义类型的话,可能会存在浅拷贝的问题,
			//所以不管T类型是否是自定义类型也好,内置类型也好
			//一律都去调用T类型的operator=
			for(size_t i = 0; i < sz; i++)
			{
				tmp[i] = _start[i];
			}
			//回收旧空间
			delete[] _start;
		}
		_start = tmp;
		_finish = _start + sz;//提前备份sz的大小,防止计算错误
		//错误示范:\_finish = \_start + size();
		//\_start 已经指向了一块新分配的空间地址就变了
		_endofstroage = _start + n;
		//实际空间开多大
	}
}

这里需要注意的是_start = tmp;的时候_start 就已经指向新的空间了,如果使用_start + size()回去调用size函数
那就成了这样了:_start + (_finish - _start),那就会很有可能是一个负数了,当然我们的编译器也不会这么傻
在这里插入图片描述
当断点执行到这一行的时候_finish指向的位置还是0x00000000,那么_finish压根就没变,解决办法,防止_start指向新空间后地址变了,我们先提前备份好size个数据

void push\_back(const T& v) 
{
	//满了考虑扩容
	if (_finish == _endofstroage) 
	{
		//尾插数据的时候提前计算增容大小
		size_t newcapacity = capacity() == 0 ? 4 
								: capacity() \* 2;
		reserve(newcapacity);
	}
	//往这块空间填充值,\_finish再往后挪动
	\*_finish = v;
	_finish++;
}

跟顺序表的尾插类似,考虑扩容就行

迭代器

typedef T\* iterator;

//返回开始位置
iterator begin() const
{ return _start; }
//返回最后一个元素的下一个位置
iterator end() const
{ return _finish; }

提一句,范围for的底层是支持迭代器的,迭代器原理类似指针

空间容量

下面的接口函数我们直接对照图就可以清晰地理解
在这里插入图片描述

//返回总空间大小
size_t capacity() const
{
	return _endofstroage - _start;
}
//返回有效数据个数
size_t size()const
{
	return _finish - _start;
}

删除

void erase() 
{
	assert(!empty());
	//指针往前挪动,就相当于减去一个数据
	--_finish;
}
//判空,如果\_start 和 \_finish都在一个起始位置就说明容器为空
bool empty() const{ return _start == _finish; }

resize

void resize(size_t n,T a = T()) 
{
	//如果n小于实际空间的大小那么就做缩容处理
	if (n < capacity()) 
	{
		_finish = _start + n;
	}
	else 
	{
		//如果n > capacity()个空间,就先扩容
		if (n > capacity()) 
		{
			reserve(n);
		}
		//空间够就对多出来的空间初始化
		while (_finish < _endofstroage) 
		{
			\*_finish++ = a;
		}
	}
}

resize的3个情况

1、指定空间小于实际空间容量,那么就做缩容处理,将空间调整到n个大小
2、size() < n < capacity() ,那么就对这段区间的空间初始化
3、n > capacity() ,先扩容到n个大小的空间,再对这段空间初始化

insert

insert在指定pos位置插入一个数据,并返沪pos位置的迭代器

//指定pos位置插入
iterator& insert(iterator pos, const T &x)
{
	//如果空间满了,先扩容再挪动数据
	if (_finish == _endofstroage) 
	{
		//计算pos偏移起始地址的长度
		size_t len = pos - _start;
		size_t newcapacity = capacity() == 0 ? 4 :
								 capacity() \* 2;
		//扩容至newcapacity个
		reserve(newcapacity);
		//扩容后pos迭代器就已经失效了,重新计算pos迭代器的位置
		pos = _start + len;
	}
	
	//将【pos,\_finish】往后挪动n + 1个位置
	iterator end = _finish - 1;
	while (end >= pos) 
	{
		\*(end + 1) = \*end;
		end--;
	}
	//存入数据
	\*pos = x;
	++_finish;
	//标准库的inert返回的是pos位置的迭代器,这里我们同样这么处理
	return pos ;
}

需要注意的是这里的insert会引发迭代器失效的问题,即使在函数内部将pos迭代器的位置处理了,但是值传递并不会影响到实参,所以外面的pos 还是失效了,需要再次重新给pos赋值,

析构函数

~vector() 
{
	if (_start) 
	{
		delete[] _start;
	}
	_start = _finish = _endofstroage = __nullptr;
}

释放空间,将_start、_finish 、_endofstroage 这三个指针置空

vector拷贝构造函数

不复用接口写法

vector(vector<T>& v)
	:\_start(nullptr)
	,\_finish(nullptr)
	,\_endofstroage(nullptr)
{
	_start = new T[v.capacity()];
	memcpy(_start,v._start,sizeof(T) \* v.size());
	_finish = _start + v.size();
	_endofstroage = _start + v.capacity();		
}

复用接口的写法

vector(vector<T>& v)
	:\_start(nullptr)
	,\_finish(nullptr)
	,\_endofstroage(nullptr)
{
	//提前开好一块够大的空间
	
	reserve(v.capacity());
	
	//拷贝一遍
	for (auto e : v) 
	{
		push\_back(e);
	}
}

区别不大,都是为了先给_start开一块新的空间,再将原数据拷贝过来,推荐第二种写法,代码更简洁

拷贝赋值运算符

//复用写法
vector<T>& operator=(const vector<T> v)	//复用拷贝构造函数
{
	if (this != &v) 
	{
		//交换this对象和v对象的成员
		swap(v);
	}
	return \*this;
}


//不复用写法
vector<T>& operator=(const vector<T> &v)
{
	if (this != &v) 
	{
		//释放旧的空间
		delete[]_start;
		//重新分配一块新的空间
		_start = new T[v.capacity()];
		//将旧空间的数据挪到新空间
		memcpy(_start,v._start,sizeof(T)\* v.size());


### 给大家的福利


**零基础入门**


对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。


![](https://img-blog.csdnimg.cn/img_convert/95608e9062782d28f4f04f821405d99a.png)


同时每个成长路线对应的板块都有配套的视频提供:


![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a91b9e8100834e9291cfcf1695d8cd42.png#pic_center)


因篇幅有限,仅展示部分资料


网络安全面试题


![](https://img-blog.csdnimg.cn/img_convert/80674985176a4889f7bb130756893764.png)


绿盟护网行动


![](https://img-blog.csdnimg.cn/img_convert/9f3395407120bb0e1b5bf17bb6b6c743.png)


还有大家最喜欢的黑客技术


![](https://img-blog.csdnimg.cn/img_convert/5912337446dee53639406fead3d3f03c.jpeg)


**网络安全源码合集+工具包**


![](https://img-blog.csdnimg.cn/img_convert/5072ce807750c7ec721c2501c29cb7d5.png)


![](https://img-blog.csdnimg.cn/img_convert/4a5f4281817dc4613353c120c9543810.png)

**所有资料共282G**,朋友们如果有需要全套《网络安全入门+黑客进阶学习资源包》,可以扫描下方二维码领取(如遇扫码问题,可以在评论区留言领取哦)~




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618540462)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值