如何更改C++ STL向量的特定元素

Moe*_*oeb 45 c++ stl vector

vector<int> l;
for(int i=1;i<=10;i++){
   l.push_back(i);
}
Run Code Online (Sandbox Code Playgroud)

现在,例如,如何5th element将矢量更改为-1

我试过l.assign(4, -1); 它的表现并不像预期的那样.其他矢量方法似乎都不合适.

我使用了矢量,因为我需要在我的代码中使用随机访问功能(使用l.at(i)).

Jam*_*lis 107

at并且operator[]都返回对索引元素的引用,因此您可以简单地使用:

l.at(4) = -1;
Run Code Online (Sandbox Code Playgroud)

要么

l[4] = -1;
Run Code Online (Sandbox Code Playgroud)

  • 你最好习惯使用`at`,少用惯用语,但绑定检查是无价的. (13认同)
  • @Matt:虽然越界错误往往是程序员错误,而`at`会引发异常.也就是说,`assert`会更好,我认为MSVC和gcc都检查了迭代器. (2认同)
  • @Milan:实际上,这应该阅读_bounds_-checking,这是检查边界的行为。如果您有一个包含 5 个元素的向量“vec”,则尝试访问索引 5 处的元素是一个错误,有效索引为 0 到 4(包括),并且 5 及以上不指向任何元素。`vec[5]` 会......做一些事情。可能会重新解释留在那里的任何字节,就好像它们是有效对象一样,或者可能更糟。这是未定义的行为,所以任何事情都可能发生,真的。然而,`vec.at(5)` 将在访问元素之前首先检查边界,并在这种情况下抛出异常。 (2认同)

rba*_*dar 12

尽管@JamesMcNellis的答案是有效的,但我想解释一些关于错误处理的事情,以及还有另一种方法可以做你想做的事情.

您有四种方法可以访问向量中的特定项:

  • 使用[]运营商
  • 使用成员函数 at(...)
  • 将迭代器与给定的偏移量结合使用
  • 使用标准C++库std::for_eachalgorithm标头.这是我可以推荐的另一种方式(它在内部使用迭代器).您可以在此处阅读有关它的更多信息.

在下面的例子中,我将使用以下载体作为实验室老鼠并解释前三种方法:

static const int arr[] = {1, 2, 3, 4};
std::vector<int> v(arr, arr+sizeof(arr)/sizeof(arr[0]));
Run Code Online (Sandbox Code Playgroud)

这会创建一个向量,如下所示:

1 2 3 4
Run Code Online (Sandbox Code Playgroud)

首先让我们来看看[]做事的方式.它的工作方式与使用普通数组时的预期方式大致相同.您提供索引并可能访问所需的项目.我说可能是因为[]操作员不检查向量是否实际上有那么多项.这导致无声的无效内存访问.例:

v[10] = 9;
Run Code Online (Sandbox Code Playgroud)

这可能会也可能不会导致即时崩溃.最糟糕的情况当然是如果它没有,你实际上得到的似乎是一个有效的值.与数组类似,这可能会导致浪费时间试图找到原因,例如1000行代码后来你得到的值100而不是234,这有点连接到你从向量中检索项目的那个位置.

更好的方法是使用at(...).这将自动检查out of bounds行为并打破投掷std::out_of_range.所以在我们拥有的情况下

v.at(10) = 9;
Run Code Online (Sandbox Code Playgroud)

我们将得到:

在抛出'std :: out_of_range'的实例后调用终止
what():vector :: _ M_range_check:__n(10)> = this-> size()(这是4)

第三种方式类似于[]操作员,你可以搞砸了.像数组一样的向量是包含相同类型数据的连续内存块序列.这意味着您可以通过将起始地址分配给迭代器来使用起始地址,然后只需向此迭代器添加偏移量.偏移量仅表示要遍历的第一个项目后的项目数:

std::vector<int>::iterator it = v.begin(); // First element of your vector
*(it+0) = 9;  // offest = 0 basically means accessing v.begin()
// Now we have 9 2 3 4 instead of 1 2 3 4
*(it+1) = -1; // offset = 1 means first item of v plus an additional one
// Now we have 9 -1 3 4 instead of 9 2 3 4
// ...
Run Code Online (Sandbox Code Playgroud)

如你所见,我们也可以做到

*(it+10) = 9;
Run Code Online (Sandbox Code Playgroud)

这又是一个无效的内存访问.这与使用基本相同,at(0 + offset)但没有超出范围的错误检查.

我建议at(...)尽可能使用,不仅因为它与迭代器访问相比更具可读性,而是因为上面提到的带有偏移量组合的迭代器和[]运算符的错误检查无效索引.