array 容器是 C++ 11 标准中新增的序列容器,简单地理解,它就是在 C++ 普通数组的基础上,添加了一些成员函数和全局函数。
之前我在思考,STL为什么要多出这个容器呢?直接用数组它不香吗??它不快吗?非要搞出这么个玩意。后来我想明白了,我觉得它就是想要使用STL里面的算法。
先把它用起来吧。
首先需要引用:
#include
array也就是数组嘛,所以它和我们常用的数组一样比如:int a[10],它表示10个整型的元素;array 容器和数组一样,它的大小也是固定的,无法动态的扩展或收缩。
std::array
但是std::array
std::array<int,10> a{};
这代表默认初始化为0.
如下测试:
再看另外的测试:
现在看看随机访问:
其中我比较喜欢的一个数可以直接拿出它的个数,不想数组一样sizeof(a)/sizeof(a[0]),ZTM麻烦,如下
赋值与交换
使用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中元素的值是未定义的
array<int, 5> arr1 = { 1, 2, 3, 4, 5 };
// ERROR: arr1 = { 1, 2, 3 }; 不能使用初始化列表来向array赋值
可以使用array的成员函数fill()来将array中的元素赋为相同的值
下面是一些常用的函数:
成员函数 | 功能 |
---|---|
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算法。
看源代码一定要看它的实现思路,原理,不要钻牛角尖。