在保留后直接分配给std :: vector不会抛出错误但不会增加矢量大小

时间:2017-12-06 05:54:56

标签: c++11 stdvector assignment-operator

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

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而不是零?

2 个答案:

答案 0 :(得分: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)

简答:

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

  

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

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

2)您只是访问了您尚未拥有的内存(reserve不是resize),您在其上执行的操作是未定义的行为。但是,你从来没有在vector中添加一个元素,也不知道你甚至修改了它的缓冲区。正如@Bill所示,允许vector更改其缓冲区而不复制本地更改。

编辑: 此外,如果使用vector::at函数,则可能因边界检查而出现异常。

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

实施例: https://ideone.com/sXnPzT