使用索引运算符处理deques和超出deque大小的问题

时间:2015-01-13 15:29:26

标签: c++ stl deque

在C ++中有一个奇怪的deques问题。 让我们说我有一个大小为4的双精灵双胞胎。出于某种原因,当使用索引运算符时,我似乎能够超过双端队列的大小。 换句话说,如果我写下以下内容,编译器和执行程序都不会barf:

for(int i = 0; i < 7; i++)
{
    x[i] = (double)(i*i);
    cout << x[i] << endl;
}

其中x是双端队列。而我实际上能够从中获得输出。 它不会增加双端队列的大小。如果我输出x.size(),我仍然得到4。 是什么赋予了? 我使用Code :: Blocks和它附带的标准默认gcc编译器。

2 个答案:

答案 0 :(得分:3)

operator[]没有检查边界,就像使用原始数组一样。 at成员函数,如果您改为使用

x.at(i);

如果超出std::out_of_range的范围,您将获得deque例外。如果您通过内存错误检查程序(如valgrind)运行原始代码,您将看到&#34;无效读取&#34;和&#34;无效写&#34;错误。

如果您查看cppreference's docs on operator[],您会看到注释&#34;不会执行边界检查。&#34;

然而the docs for at()

  

如果pos不在容器范围内,则抛出类型为std :: out_of_range的异常

离开容器的界限是未定义的行为。如果您使用索引进行访问而不确定其是否在入境,那么您的工作就是检查它是否正确,或者使用at并且可能使用{{1}}处理异常。

答案 1 :(得分:1)

索引超出范围会产生未定义的行为,因此任何事情都可能发生。

许多容器会将当前大小舍入到一些方便的值(例如,2的幂),因此根据当前大小,您将在集合中的最后一项之后拥有一定量的内存。索引到该内存并尝试读取它会产生一些结果,但内存通常是未初始化的,因此结果通常是无意义和无效的(并且,尽管大多数情况下,容器可以进行边界检查,并抛出当你索引越界时,一个异常或几乎任何其他东西。)

IMO,at是处理这种可能性的相当差的工具。避免此类问题的更好方法是基于范围的for循环:

for (auto &d : x) {
    d = d * d;
    std::cout << d << "\n"; // avoid `endl`, which flushes the stream.
}

另一种可能性是使用标准算法:

std::transform(x.begin(), x.end(), x.begin(), [](double d) { return d*d; });
std::copy(x.begin(), x.end(), std::ostream_iterator<double>(std::cout, "\n"));

还有基于范围的算法(例如,Boost中的一个集合,至少还有一个建议用于未来的C ++标准),(do / would)允许按以下顺序执行某些操作:

copy(x, output_range<double>(std::cout, "\n"));

由于这会自己找出x的范围,缺少范围代码中的错误,因此很难以这种方式意外地索引越界。