我希望这不是一个太有争议的问题,但我找不到关于SO的适当完整答案。这也不是关于reserve
和resize
方法之间的区别或capacity
和size
之间的区别的问题,这对我来说(希望)很清楚,并且经常被问到SO。此外,这不是一个问题,如果这是好的做法,但事实并非如此!
请考虑以下情况:
#include <vector>
#include <iostream>
struct Foo
{
double a, b;
};
int main(int argc, char* argv[])
{
std::vector<Foo> Vec;
Vec.reserve(100);
Foo foo;
foo.a = -13.131;
foo.b = 3.141;
for(int i = 0; i < 100; ++i)
Vec[i] = foo;
for(int i = 0; i < 100; ++i)
std::cout << Vec[i].a << std::endl;
return 0;
}
我首先创建了一个 Foo 和保留内存std::vector
,但不调整矢量的大小。显然size() = 0
,但是 100 个元素的内存已经分配,现在可以由我的程序自由使用,所以从技术上讲,从内存中写入和读取这些元素的任何位置都不能导致分段错误,对吗?
我尝试在 Ubuntu 14.04 上运行此代码,并且一切按预期工作,所有 100 个元素都已成功写入,所有输出也是 -13.131,即使向量大小保持在 0。如果我在 SO 上寻找许多答案,它们都正确地指出它会导致未定义的行为,因为元素没有初始化,但它实际上会导致以任何方式导致分割错误(不是谈论访问向量中单位化指针的元素等)?
这里提出了一个类似的问题,这似乎证实了我的想法,但它原则上是否适用于所有支持C++编译的平台?
一旦你有未定义的行为,它就是未定义的行为。 未定义行为的一个关键方面是,您无法确定不同系统和编译器上的行为是什么。现在,您可以查看特定编译器和特定库实现的代码,您将看到它按预期运行。
但我认为你不会找到任何愿意打赌这将适用于所有不同系统、编译器和库实现的人。
例如,如果特定的矢量实现决定将保留内存用于内部信息怎么办?也许不太可能,但你怎么能确定没有系统真正这样做呢?
让我们考虑一个具体的例子 - std::vector 实现,当调用 reserve() 时,它会分配内存,但随后开始在后台线程上执行复制 - 因为它可以...耸耸肩谁知道在不久的将来会发生什么! 因此,在复制时,所有读取都将解锁并直接进入旧内存区域,因为这仍然有利于读取。
现在尝试读取超出范围的内容将尝试读取随机内存,而不是您断言的应该是新分配的内存。
因此,正如评论和其他答案所说,未定义就是未定义。