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

Directly assigning to a std::vector after reserving does not throw error but does not increase vector size

提问人:blipblop 提问时间:12/6/2017 更新时间:8/29/2023 访问量:77

问:

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

class C
{
int ID = 0;

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

int getID()
{
    return ID;
}
};

假设您创建一个空元素,然后保留它以容纳 10 个元素:std::vector<C>

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

现在,将 的新实例分配到向量的索引 4 中:C

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,那么向量不应该有 size = 1 而不是 0 吗?

C++11 stdvector 赋值运算符

评论

0赞 underscore_d 12/6/2017
vector::resize() 和 vector::reserve() 之间的 Choice 可能重复
0赞 blipblop 12/7/2017
@underscore_d 我的问题绝对不可能是那个问题的重复。不是很远。我很清楚该做什么和做什么。我的问题是关于调用后的行为。reserveresizeatreserve
0赞 underscore_d 12/7/2017
“我的问题是关于at的行为”不是很远。当它甚至一次都没有提到这种方法时,它的行为到底是怎么回事?如果您使用 ,该程序将发出明显的错误信号,而不是产生未定义的行为。无论如何,这个问题是一个欺骗性的 IMO,因为您的问题归结为询问除了 - 包括和索引 - 之外的特定方式是否可以增加,以及索引到未分配的点是否可能,但事实并非如此。正如会告诉你的.at().at().resize().reserve()operator[].size().reserve().at()

答:

2赞 Bill Lynch 12/6/2017 #1

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

#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.
}

评论

0赞 blipblop 12/6/2017
谢谢你的回答。但是,我不认为它涵盖了我的实际示例。在代码中,您没有分配一个新的 Foo 对象,例如 。如果你这样做了,那么是的,它将始终打印,但向量的大小仍然为零。所有这些都不会让编译器抛出任何错误。foos[4] = Foo()3
1赞 R2RT 12/6/2017 #2

简短的回答:

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

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

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

  1. 你只是访问了你还不拥有的内存(不是),你对它所做的任何事情都是未定义的行为。但是,您从未添加过元素,它甚至不知道您修改了它的缓冲区。如@Bill所示,允许在不复制本地更改的情况下更改其缓冲区。reserveresizevectorvector

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

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

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