保留后直接分配给 std::vector 不会引发错误,但不会增加向量大小

问题描述 投票:0回答:2

让我们创建一个辅助类来帮助可视化问题:

class C
{
int ID = 0;

public:
C(const int newID)
{
    ID = newID;
}

int getID()
{
    return ID;
}
};

假设您创建一个空的

std::vector<C>
,然后保留它来容纳 10 个元素:

std::vector<C> pack;
pack.reserve(10);
printf("pack has  %i\n", pack.size()); //will print '0'

现在,将

C
的新实例分配给向量的索引 4:

pack[4] = C(57);
printf("%i\n", pack[4].getID()); //will print '57'
printf("pack has  %i\n", pack.size()); //will still print '0'

我发现这里有两件事很奇怪:

1) 即使在发布模式下,分配是否也不应该使编译器(Visual Studio 2015,发布模式)抛出错误?

2) 因为它没有,并且元素实际上存储在位置 4,所以向量不应该具有大小 = 1 而不是零吗?

c++11 stdvector assignment-operator
2个回答
2
投票

未定义的行为仍然是未定义的。如果我们将其作为对象向量,您会更清楚地看到意想不到的行为。

#include <iostream>
#include <vector>

struct Foo {
  int data_ = 3;
};

int main() {
  std::vector<Foo> foos;
  foos.reserve(10);
  std::cout << foos[4].data_; // This probably doesn't output 3.
}

在这里,我们可以看到,因为我们还没有真正分配对象,所以构造函数还没有运行。

另一个例子,由于您使用的是向量尚未实际开始分配给您的空间,因此如果向量需要重新分配其后备内存,则您写入的值将不会被复制。

#include <iostream>
#include <vector>

int main() {
  std::vector<int> foos;
  foos.reserve(10);
  foos[4] = 100;
  foos.reserve(10000000);
  std::cout << foos[4]; // Probably doesn't print 100.
}

1
投票

简短回答:

  1. 没有理由抛出异常,因为
    operator[]
    不应该验证您已经通过的位置。它可能在调试模式下这样做,但肯定不会在发布模式下这样做(否则性能会受到影响)。在发布模式下,编译器相信您提供的代码是防错的,并会尽一切努力使您的代码更快。

返回对指定位置 pos 处元素的引用。 没有 执行边界检查。

http://en.cppreference.com/w/cpp/container/vector/operator_at

  1. 您只是访问了您尚未拥有的内存(
    reserve
    不是
    resize
    ),您对其执行的任何操作都是未定义的行为。但是,您从未将元素添加到
    vector
    中,并且它不知道您甚至修改了其缓冲区。正如@Bill 所示,
    vector
    可以更改其缓冲区,而无需复制本地更改。

编辑: 另外,如果您使用

vector::at
函数,您可能会因边界检查而出现异常。

即:

pack.at(4) = C(57);
抛出异常

示例: https://ideone.com/sXnPzT

© www.soinside.com 2019 - 2024. All rights reserved.