STL-02-array使用及其源码剖析

array 容器是 C++ 11 标准中新增的序列容器,简单地理解,它就是在 C++ 普通数组的基础上,添加了一些成员函数和全局函数。

之前我在思考,STL为什么要多出这个容器呢?直接用数组它不香吗??它不快吗?非要搞出这么个玩意。后来我想明白了,我觉得它就是想要使用STL里面的算法。

先把它用起来吧。

首先需要引用:

#include 

array也就是数组嘛,所以它和我们常用的数组一样比如:int a[10],它表示10个整型的元素;array 容器和数组一样,它的大小也是固定的,无法动态的扩展或收缩。

std::array a;这个和int a[10];一样。

但是std::array a;和int a[10];一样,都没有默认初始化。

std::array<int,10> a{}

这代表默认初始化为0.

如下测试:

STL-02-array使用及其源码剖析_第1张图片
默认初始化为0.

再看另外的测试:

STL-02-array使用及其源码剖析_第2张图片
从结果可以看出和数组特性一模一样。

现在看看随机访问:

STL-02-array使用及其源码剖析_第3张图片

其中我比较喜欢的一个数可以直接拿出它的个数,不想数组一样sizeof(a)/sizeof(a[0]),ZTM麻烦,如下
STL-02-array使用及其源码剖析_第4张图片
赋值与交换
使用array的一个好处就是可以将一个相同类型的array对象赋值给另一个对象,array中的元素将会依次进行复制,因此,在赋值之后,两个array具有相同的元素,但需要保证两个array具有相同的元素类型以及大小。但对于一般数组,则不能这样做:

array<int, 5> arr1 = { 1, 2, 3, 4, 5 };
array<int, 5> arr2 = { 5, 4, 3, 2, 1 };
array<int, 5> arr3 = arr1;                  // arr3 包含:1 2 3 4 5
arr3 = arr2;                                // arr3 包含:5 4 3 2 1 
 array<int, 10> a;         // arr中元素的值是未定义的
 

STL-02-array使用及其源码剖析_第5张图片
不能使用初始化列表来向array赋值

array<int, 5> arr1 = { 1, 2, 3, 4, 5 };
// ERROR: arr1 = { 1, 2, 3 }; 不能使用初始化列表来向array赋值
 

可以使用array的成员函数fill()来将array中的元素赋为相同的值
STL-02-array使用及其源码剖析_第6张图片

下面是一些常用的函数:

成员函数 功能
begin() 返回指向容器中第一个元素的随机访问迭代器。
end() 返回指向容器最后一个元素之后一个位置的随机访问迭代器,通常和 begin() 结合使用。
rbegin() 返回指向最后一个元素的随机访问迭代器。
rend() 返回指向第一个元素之前一个位置的随机访问迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上增加了 const 属性,不能用于修改元素。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
size() 返回容器中当前元素的数量,其值始终等于初始化 array 类的第二个模板参数 N。
max_size() 返回容器可容纳元素的最大数量,其值始终等于初始化 array 类的第二个模板参数 N。
empty() 判断容器是否为空,和通过 size()==0 的判断条件功能相同,但其效率可能更快。
at(n) 返回容器中 n 位置处元素的引用,该函数自动检查 n 是否在有效的范围内,如果不是则抛出 out_of_range 异常。
front() 返回容器中第一个元素的直接引用,该函数不适用于空的 array 容器。
back() 返回容器中最后一个元素的直接应用,该函数同样不适用于空的 array 容器。
data() 返回一个指向容器首个元素的指针。利用该指针,可实现复制容器中所有元素等类似功能。
fill(val) 将 val 这个值赋值给容器中的每个元素。
array1.swap(array2) 交换 array1 和 array2 容器中的所有元素,但前提是它们具有相同的长度和类型。

上面是一些常用的函数。使用方法也有说明。

接下来看看源码:

这个源码是我VS2013下的,在c盘就有,自己可以拿来学习。
详细路径为:C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\array
我直接将解释写在后面吧

注意源码后面我有部分源代码解释。

template<class _Ty,//函数模板,表明类型,如int
	size_t _Size>//表明几个元素
	class array
	{	// fixed size array of values
public:
	enum {_EEN_SIZE = _Size};	// helper for expression evaluator
	typedef array<_Ty, _Size> _Myt;
	typedef _Ty value_type;
	typedef size_t size_type;
	typedef ptrdiff_t difference_type;
	typedef _Ty *pointer;
	typedef const _Ty *const_pointer;
	typedef _Ty& reference;
	typedef const _Ty& const_reference;

	typedef _Array_iterator<_Ty, _Size> iterator;
	typedef _Array_const_iterator<_Ty, _Size> const_iterator;

	typedef _STD reverse_iterator<iterator> reverse_iterator;
	typedef _STD reverse_iterator<const_iterator> const_reverse_iterator;

	void assign(const _Ty& _Value)
		{	// assign value to all elements
		_Fill_n(_Elems, _Size, _Value);
		}

	void fill(const _Ty& _Value)
		{	// assign value to all elements
		_Fill_n(_Elems, _Size, _Value);
		}

	void swap(_Myt& _Other)
		_NOEXCEPT_OP(_NOEXCEPT_OP(_Swap_adl(_Elems[0], _Elems[0])))
		{	// swap contents with _Other
		_Swap_ranges(_STD addressof(_Elems[0]),
			_STD addressof(_Elems[0]) + _Size,
			_STD addressof(_Other._Elems[0]));
		}

	iterator begin() _NOEXCEPT
		{	// return iterator for beginning of mutable sequence
		return (iterator(_STD addressof(_Elems[0]), 0));
		}

	const_iterator begin() const _NOEXCEPT
		{	// return iterator for beginning of nonmutable sequence
		return (const_iterator(_STD addressof(_Elems[0]), 0));
		}

	iterator end() _NOEXCEPT
		{	// return iterator for end of mutable sequence
		return (iterator(_STD addressof(_Elems[0]), _Size));
		}

	const_iterator end() const _NOEXCEPT
		{	// return iterator for beginning of nonmutable sequence
		return (const_iterator(_STD addressof(_Elems[0]), _Size));
		}

	reverse_iterator rbegin() _NOEXCEPT
		{	// return iterator for beginning of reversed mutable sequence
		return (reverse_iterator(end()));
		}

	const_reverse_iterator rbegin() const _NOEXCEPT
		{	// return iterator for beginning of reversed nonmutable sequence
		return (const_reverse_iterator(end()));
		}

	reverse_iterator rend() _NOEXCEPT
		{	// return iterator for end of reversed mutable sequence
		return (reverse_iterator(begin()));
		}

	const_reverse_iterator rend() const _NOEXCEPT
		{	// return iterator for end of reversed nonmutable sequence
		return (const_reverse_iterator(begin()));
		}

	const_iterator cbegin() const _NOEXCEPT
		{	// return iterator for beginning of nonmutable sequence
		return (((const _Myt *)this)->begin());
		}

	const_iterator cend() const _NOEXCEPT
		{	// return iterator for end of nonmutable sequence
		return (((const _Myt *)this)->end());
		}

	const_reverse_iterator crbegin() const _NOEXCEPT
		{	// return iterator for beginning of reversed nonmutable sequence
		return (((const _Myt *)this)->rbegin());
		}

	const_reverse_iterator crend() const _NOEXCEPT
		{	// return iterator for end of reversed nonmutable sequence
		return (((const _Myt *)this)->rend());
		}

	size_type size() const _NOEXCEPT
		{	// return length of sequence
		return (_Size);
		}

	size_type max_size() const _NOEXCEPT
		{	// return maximum possible length of sequence
		return (_Size);
		}

	bool empty() const _NOEXCEPT
		{	// test if sequence is empty
		return (_Size == 0);
		}

	reference at(size_type _Pos)
		{	// subscript mutable sequence with checking
		if (_Size <= _Pos)
			_Xran();
		return (_Elems[_Pos]);
		}

	const_reference at(size_type _Pos) const
		{	// subscript nonmutable sequence with checking
		if (_Size <= _Pos)
			_Xran();
		return (_Elems[_Pos]);
		}

	reference operator[](size_type _Pos)
		{	// subscript mutable sequence
 #if _ITERATOR_DEBUG_LEVEL == 2
		if (_Size <= _Pos)
			_DEBUG_ERROR("array subscript out of range");

 #elif _ITERATOR_DEBUG_LEVEL == 1
		_SCL_SECURE_VALIDATE_RANGE(_Pos < _Size);
 #endif /* _ITERATOR_DEBUG_LEVEL */

		_Analysis_assume_(_Pos < _Size);

		return (_Elems[_Pos]);
		}

	const_reference operator[](size_type _Pos) const
		{	// subscript nonmutable sequence
 #if _ITERATOR_DEBUG_LEVEL == 2
		if (_Size <= _Pos)
			_DEBUG_ERROR("array subscript out of range");

 #elif _ITERATOR_DEBUG_LEVEL == 1
		_SCL_SECURE_VALIDATE_RANGE(_Pos < _Size);
 #endif /* _ITERATOR_DEBUG_LEVEL */

		_Analysis_assume_(_Pos < _Size);

		return (_Elems[_Pos]);
		}

	reference front()
		{	// return first element of mutable sequence
		return (_Elems[0]);
		}

	const_reference front() const
		{	// return first element of nonmutable sequence
		return (_Elems[0]);
		}

	reference back()
		{	// return last element of mutable sequence
		return (_Elems[_Size - 1]);
		}

	const_reference back() const
		{	// return last element of nonmutable sequence
		return (_Elems[_Size - 1]);
		}

	_Ty *data() _NOEXCEPT
		{	// return pointer to mutable data array
		return (_Elems);
		}

	const _Ty *data() const _NOEXCEPT
		{	// return pointer to nonmutable data array
		return (_Elems);
		}

	__declspec(noreturn) void _Xran() const
		{	// report an out_of_range error
		_Xout_of_range("invalid array subscript");
		}

	_Ty _Elems[_Size == 0 ? 1 : _Size];
	};

可以看到这个里面没有分配器(内存管理器),直接用的数组。
其实这源码很简单,就挑出几个吧:
如下伪代码:

首先看:

这个就是存储元素的

_Ty _Elems[_Size == 0 ? 1 : _Size];

它很智能如果是0个元素那么就给它调整为1个,当然不能为0啊,可以这样想,int a[0];这显然是不行的。

还有这一胡片的重定义,需要你花点时间将其对号入座:

typedef array<_Ty, _Size> _Myt;
	typedef _Ty value_type;
	typedef size_t size_type;
	typedef ptrdiff_t difference_type;
	typedef _Ty *pointer;
	typedef const _Ty *const_pointer;
	typedef _Ty& reference;
	typedef const _Ty& const_reference;
reference at(size_type _Pos)
		{	// subscript mutable sequence with checking
		if (_Size <= _Pos)
			_Xran();
		return (_Elems[_Pos]);
		}

首先对访问位置进行判读,如果大于整个数组的大小,就发出异常。
如果没有就放回该位置的引用。所以我们才能有如下操作:
a.at(2) = 100;

同理如下;

	reference operator[](size_type _Pos)
		{	// subscript mutable sequence
 #if _ITERATOR_DEBUG_LEVEL == 2
		if (_Size <= _Pos)
			_DEBUG_ERROR("array subscript out of range");

 #elif _ITERATOR_DEBUG_LEVEL == 1
		_SCL_SECURE_VALIDATE_RANGE(_Pos < _Size);
 #endif /* _ITERATOR_DEBUG_LEVEL */

		_Analysis_assume_(_Pos < _Size);

		return (_Elems[_Pos]);
		}

前面的一堆都是检查,我相信你看到过:array subscript out of range,就是从这里弹出的。最后一行我相信你能看懂,不就一个数组嘛。返回相应的元素。

接着有:

_Ty *data() _NOEXCEPT
		{	// return pointer to mutable data array
		return (_Elems);
		}

可以看出也就是返回了数组的首元素地址,那也就是第一个元素的地址嘛。

iterator begin() _NOEXCEPT
		{	// return iterator for beginning of mutable sequence
		return (iterator(_STD addressof(_Elems[0]), 0));
		}

在比如,这个也就是返回数组的首元素嘛,然后再转化为typedef _Array_iterator<_Ty, _Size> iterator;这种迭代器类型。

所以说这个是最简单的。

接下来看看它加入容器的好处:
STL-02-array使用及其源码剖析_第7张图片
我们可以轻松的使用STL算法。

看源代码一定要看它的实现思路,原理,不要钻牛角尖。

你可能感兴趣的:(STL)