STL vector底层揭秘:从构造到迭代器失效

STL:一篇带你搞懂vector

vector底层实现

1.constructor与destructor

vector的构造包括默认构造普通的构造函数拷贝构造以及使用迭代区间进行构造。下面我们逐一分析这几种构造方式。

cpp 复制代码
vector()
{
	_start = _finish = nullptr;
	_end_of_storage = nullptr;
}
vector(int n, const T& val = T())
{
	for (int i = 0; i < n; i++)
	{
		push_back(val);
	}
}
template<class Inputiterator>
vector(Inputiterator first, Inputiterator last)
{
	Inputiterator begin = first;
	while (begin != last)
	{
		push_back(*begin);
		begin++;
	}
}
vector(const vector<T>& v)
{
	for (auto& e : v)
	{
		push_back(e);
	}
}
void swap(vector<T>& v)
{
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_end_of_storage, v._end_of_storage);
}
vector<T>& operator=(vector<T> v)//这里专门没有使用引用
{
	swap(v);
	return *this;
}
~vector()
{
	delete[] _start;
	_start = _finish = _end_of_storage = nullptr;
}

这里要着重说一下:T(),它对于内置类型,自动初始化为0;对于自定义类型,会调用自己的默认构造。

赋值操作 :采用现代写法,交换地址,同时重载函数的参数并没有使用引用,这样可以在调用完重载函数,自动实现对资源的释放。

2.迭代

vector的底层本身就是数组,所以它迭代器的底层就是指针,我们可以使用三个迭代器(start,finish,end_of_storage)来控制这个数组(顺序表)。

cpp 复制代码
template<class T>
class vector
{
	
public:
	typedef T* iterator;
	typedef const T* const_iterator;
	///////////////////////////////////////////////
	//迭代器
	iterator begin()
	{
		return _start;
	}
	iterator end()
	{
		return _finish;
	}
	const_iterator begin()const
	{
		return _start;
	}
	const_iterator end()const
	{
		return _finish;
	}
private:
	iterator _start = nullptr;
	iterator _finish = nullptr;
	iterator _end_of_storage = nullptr;
}

3.capacity

cpp 复制代码
size_t size()const
{
	return _finish - _start;
}
size_t capacity()const
{
	return _end_of_storage - _start;
}
void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t old_size = size();
		T* tmp = new T[n];
		//这里如果是自定义类型,就是错误的,因为这里仅仅是浅拷贝
		/*memcpy(tmp, _start, sizeof(T) * size());
		delete[] _start;*/
		//这里要进行拷贝构造
		if (_start)
		{
			for (size_t i = 0; i < n; i++)
			{
				tmp[i] = _start[i];
			}
			delete _start;
		}
		_start = tmp;
		_finish = _start + old_size;
		_end_of_storage = _start + n;
}
void resize(size_t n, const T& val = T())
{
	if (n > capacity())
	{
		reserve(n);
	}
	for (size_t i = size(); i < n; i++)
	{
		_start[i] = val;
	}
	_finish = _start + n;
}

vector的扩容问题:vs(STL为pj版本)以1.5倍进行扩容,g++(STL为SGI版本)以2倍扩容。

memcpy问题:memcpy是对内存中的内容进行一字节一字节拷贝,如果是内置类型,没有任何问题;如果是自定义类型,这就是浅拷贝,释放空间时也会导致拷贝的元素释放,进而报错。

4.modifier

4.1 push_back

cpp 复制代码
void push_back(const T& val)
{
	if (size() == capacity())
	{
		reserve(capacity() == 0 ? 4 : 2 * capacity());
	}
	*_finish = val;
	_finish++;
}

4.4 pop_back

cpp 复制代码
void pop_back()
{
	assert(_start != _finish);
	_finish--;
}

4.3 insert

cpp 复制代码
iterator insert(iterator pos, const T& val)
{
	assert(pos >= _start);
	assert(pos <= _finish);
	if (size() == capacity())
	{
		size_t n = pos - _start;
		reserve(capacity() == 0 ? 4 : 2 * capacity());
		pos = _start + n;//防止迭代器失效
	}
	
	iterator end = _finish - 1;
	while (end >= pos)
	{
		*(end + 1) = *(end);
		end--;
	}
	*pos = val;
	_finish++;
	return pos;
}

4.4 erase

cpp 复制代码
iterator erase(iterator pos)
{
	assert(pos >= _start);
	assert(pos < _finish);
	iterator begin = pos + 1;
	while (begin < _finish)
	{
		*(begin - 1) = *(begin);
		begin++;
	}
	_finish--;
	return pos;
}

迭代器失效

1.对于 insert 来说,如果容量满了的话,那么就需要扩容,原来的pos就会被释放掉,进而导致访问时就是随机数。

2.对于 erase 来说,pos原本指向的值已经被后一个值覆盖,因此返回的pos是指向下一个值的,如果删除的是末尾的值话,pos指向的值就已经越界,不能进行解引用或自增操作,因为已经越界。

3.对于底层有内存的改变,pos位置都有可能出现迭代器失效问题。

解决方法:更新迭代器。

5.access

cpp 复制代码
T& operator[](size_t pos)
{
	assert(pos < size());
	return _start[pos];
}
const T& operator[](size_t pos)const
{
	assert(pos < size());
	return _start[pos];
}

---------------------------------------------------------------------

完结啦🎇

相关推荐
大模型码小白1 小时前
ChatGPT 的回答也能被结构化抓取?AI Bot Scraper 对比测评
服务器·开发语言·数据库·人工智能·spring·chatgpt
尘中远1 小时前
给C++工业软件搭建 Agent
开发语言·c++·qt·ai·agent
shehuiyuelaiyuehao1 小时前
算法37,位运算,两个整数之和(不用+符号)
算法
2401_868534781 小时前
网规备考_2.4 路由协议
c++·设计模式
猎头南楼1 小时前
MFC 质量流量控制器中的控制算法设计与落地思考
人工智能·算法
来一碗刘肉面1 小时前
平衡二叉树的删除
数据结构·算法
啦啦啦啦啦zzzz1 小时前
自定义日志缓冲区(上)c++20
linux·服务器·c++·网络编程
wabs6662 小时前
关于二叉树【力扣199.二叉树的右视图的思考】
数据结构·c++·算法·leetcode·二叉树·层序遍历
a187927218312 小时前
【算法】动态规划第三篇:背包第一课——贪心之死与正序倒序开关
算法·leetcode·动态规划·贪心·dp·暴力·算法讲解