当要获取 std::vector 的第 n 个元素,下面几种方式都可以:
std::vector<int> vec;
size_t n = 1;
int & i = vec[n];
int & j = *(vec.begin() + n);
int & k = vec.at(n);
但是如果 n 超过了 vector 的下标范围,在几种方式的结果就有区别了。
在 linux 平台只有 at(n) 会抛出 std::out_of_range 异常,而其他两个都不会。
查看标准模板库代码,可以看到:
// element access
/**
* @brief Subscript access to the data contained in the %vector.
* @param n The index of the element for which data should be
* accessed.
* @return Read/write reference to data.
*
* This operator allows for easy, array-style, data access.
* Note that data access with this operator is unchecked and
* out_of_range lookups are not defined. (For checked lookups
* see at().)
*/
reference
operator[](size_type __n)
{ return *(this->_M_impl._M_start + __n); }
有说明:通过 operator[] 获取数组元素,不会检查下标有效性,需要检查的时候使用 at 接口
在 windows 平台 VS 环境下,都会抛出异常,VC 下的标准库是这样现实的:
reference operator[](size_type _Pos)
{ // subscript mutable sequence
#if _HAS_ITERATOR_DEBUGGING
if (size() <= _Pos)
{
_DEBUG_ERROR("vector subscript out of range");
_SCL_SECURE_OUT_OF_RANGE;
}
#endif /* _HAS_ITERATOR_DEBUGGING */
_SCL_SECURE_VALIDATE_RANGE(_Pos < size());
return (*(_Myfirst + _Pos));
}
在 Debug 配置下, _HAS_ITERATOR_DEBUGGING 默认定义为 1,但是即使强制定义为 0,也有异常,因为还有一个 _SCL_SECURE_VALIDATE_RANGE 检查。所以即使在 Release 配置下,下标检查也是存在的。
如果把上面的代码中的“引用”符号去掉,像这样:
std::vector<int> vec;
size_t n = 1;
int i = vec[n];
int j = *(vec.begin() + n);
int k = vec.at(n);
那么 at(n) 仍然会抛出 std::out_of_range 异常,其他两个会出现什么情况呢,也许是内存访问异常,也许是其他诡异的错误。
在 vec 非空的情况下,即使下标越界,也有可能对应的内存是可读写的,至于读到的是什么内容,或者写到什么地方,就是随机事件了。
所以尽量用 at 接口,让错误尽早暴露出来。