vector源码阅读笔记(push_back())

本文详细解读了C++标准库中的vector容器,包括其内部实现原理、核心函数push_back、emplace_back的运行机制,以及如何高效地进行内存分配与释放。重点阐述了vector的内存管理策略、孤儿迭代器的概念、构造函数的应用,以及emplace_back相较于push_back的优势,旨在为读者提供全面而深入的理解。
部署运行你感兴趣的模型镜像

成员变量:

	pointer _Myfirst;	// pointer to beginning of array
	pointer _Mylast;	// pointer to current end of sequence
	pointer _Myend;	// pointer to end of array
	_Alty _Alval;	// allocator object for values



对于void push_back(_Ty &&_Val)函数,会判断push_back的对象是否在vector内部(是否在内部只有在将新成员添加到vector时的传参有区别,其他没啥区别,就不分开描述了)。

首先,判断当前的大小是否足够添加成员,若已满,则_Reserve(1),申请新的内存空间。

源码如下:

	void push_back(_Ty&& _Val)
		{	// insert element at end
		if (_Inside(_STD addressof(_Val)))
			{	// push back an element
			size_type _Idx = _STD addressof(_Val) - this->_Myfirst;
			if (this->_Mylast == this->_Myend)
				_Reserve(1);
			_Orphan_range(this->_Mylast, this->_Mylast);
			_Cons_val(this->_Alval,
				this->_Mylast,
				_STD forward<_Ty>(this->_Myfirst[_Idx]));
			++this->_Mylast;
			}
		else
			{	// push back a non-element
			if (this->_Mylast == this->_Myend)
				_Reserve(1);
			_Orphan_range(this->_Mylast, this->_Mylast);
			_Cons_val(this->_Alval,
				this->_Mylast,
				_STD forward<_Ty>(_Val));
			++this->_Mylast;
			}
		}



当调用void _Reserve(size_type _Count)函数时,先获取当前的_Size,(使用this->_Mylast - this->_Myfirst得到),判断当前_Capacity是否足以使_Size增长_Count大小。若够,则直接修改_Size,否则调用size_t _Grow_to(size_type _Count)函数将当前_Capacity增加50%。若增长50%会超过max_size()(即_Alval.max_size())或依然小于_Count,则直接将capacity增长至size。

代码如下:

	void _Reserve(size_type _Count)
		{	// ensure room for _Count new elements, grow exponentially
		size_type _Size = size();
		if (max_size() - _Count < _Size)
			_Xlen();
		else if ((_Size += _Count) <= capacity())
			;
		else
			reserve(_Grow_to(_Size));
		}

	size_type _Grow_to(size_type _Count) const
		{	// grow by 50% or at least to _Count
		size_type _Capacity = capacity();

		_Capacity = max_size() - _Capacity / 2 < _Capacity
			? 0 : _Capacity + _Capacity / 2;	// try to grow by 50%
		if (_Capacity < _Count)
			_Capacity = _Count;
		return (_Capacity);
		}

在进行reserve()时,若需求的空间大小大于最大值,则调用__declspec(noreturn) void _Xlen() const记录一个错误。若大小符合,先用_Ptr指向新申请的_Count大小的内存空间,然后将原有数据全部复制到_Ptr所指向的空间中,逐个调用析构函数,再删除原有空间。最后在函数尾端修改end,first,last的值,使其指向新空间,并满足新空间的大小。

代码如下所示:

	void reserve(size_type _Count)
		{	// determine new minimum length of allocated storage
		if (max_size() < _Count)
			_Xlen();	// result too long
		else if (capacity() < _Count)
			{	// not enough room, reallocate
			pointer _Ptr = this->_Alval.allocate(_Count);

			_TRY_BEGIN
			_Umove(this->_Myfirst, this->_Mylast, _Ptr);
			_CATCH_ALL
			this->_Alval.deallocate(_Ptr, _Count);
			_RERAISE;
			_CATCH_END

			size_type _Size = size();
			if (this->_Myfirst != 0)
				{	// destroy and deallocate old array
				_Destroy(this->_Myfirst, this->_Mylast);
				this->_Alval.deallocate(this->_Myfirst,
					this->_Myend - this->_Myfirst);
				}

			this->_Orphan_all();
			this->_Myend = _Ptr + _Count;
			this->_Mylast = _Ptr + _Size;
			this->_Myfirst = _Ptr;
			}
		}
	__declspec(noreturn) void _Xlen() const
		{	// report a length_error
		_Xlength_error("vector<T> too long");
		}



申请空间的代码如下:

template<class _Ty> inline
	_Ty _FARQ *_Allocate(_SIZT _Count, _Ty _FARQ *)
	{	// allocate storage for _Count elements of type _Ty
	void *_Ptr = 0;

	if (_Count <= 0)
		_Count = 0;
	else if (((_SIZT)(-1) / sizeof (_Ty) < _Count)
		|| (_Ptr = ::operator new(_Count * sizeof (_Ty))) == 0)
		_THROW_NCEE(bad_alloc, 0);

	return ((_Ty _FARQ *)_Ptr);
	}



对于下面这条代码,我没看懂……看翻译,貌似是什么“孤儿迭代器”,没搞明白

_Orphan_range(this->_Mylast, this->_Mylast);


接下来是

_Cons_val(this->_Alval, this->_Mylast, _STD forward<_Ty>(_Val));
++this->_Mylast;

意思是将_Val的值存储到this->_Mylast所指向的空间中,并将_Mylast向后指一位。



源码如下:

		// TEMPLATE FUNCTIONS _Cons_val AND _Dest_val
template<class _Alloc,
	class _Ty1,
	class _Ty2>
	void _Cons_val(_Alloc& _Alval, _Ty1 *_Pdest, _Ty2&& _Src)
	{	// construct using allocator
	_Alval.construct(_Pdest, _STD forward<_Ty2>(_Src));
	}

所谓的_Ty &&是“右值引用“


vector还有一个功能与void push_back(_Ty&& _Val)类似的函数:

void emplace_back(_Ty&& _Val)

这个函数与void push_back(_Ty&& _Val)的功能类似,均为在vector尾端添加一个元素,但void emplace_back(_Ty&& _Val)相对push_back来可以避免额外的复制与移动操作

参见:blog.youkuaiyun.com/caroline_wendy/article/details/12967193



您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

C++ 中,使用 `vector` 构建二维数组时,可以通过嵌套的 `vector` 类型来实现。例如,`vector<vector<int>>` 表示一个整数类型的二维数组。在这种结构中,可以利用 `push_back()` 方法动态地向每一行中添加元素。 如果外层 `vector` 已经被初始化,并且内层 `vector` 没有指定大小,则可以直接使用 `push_back()` 向其中添加元素。以下是一个示例代码,展示了如何创建一个二维 `vector` 并使用 `push_back()` 添加元素: ```cpp #include <iostream> #include <vector> int main() { // 创建一个空的 vector<vector<int>> std::vector<std::vector<int>> matrix; // 向 matrix 中添加一个新的行,并且该行是一个空的 vector<int> matrix.push_back(std::vector<int>()); // 现在向最新添加的行中添加元素 matrix.back().push_back(1); matrix.back().push_back(2); matrix.back().push_back(3); // 打印 matrix 的内容 for (const auto &row : matrix) { for (int val : row) { std::cout << val << " "; } std::cout << std::endl; } return 0; } ``` 在这个例子中,首先调用 `matrix.push_back(std::vector<int>())` 来向 `matrix` 中添加一行,然后通过 `matrix.back()` 获取最后一行(即新添加的那一行),并使用 `push_back()` 在这一行中添加具体的数值 [^4]。 当需要为二维数组的不同行添加不同数量的元素时,这种方法特别有用,因为它允许每行具有不同的长度。这种灵活性来自于 `vector` 容器能够根据需要自动调整其大小的能力 [^5]。 此外,还可以预先分配一定的大小给内层的 `vector`,但这通常用于所有行都有相同数量的列的情况下。如果不希望所有的行都有相同的列数,则应该避免预分配大小,而是直接使用 `push_back()` 动态添加元素 [^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值