为什么在 C++ 中对向量使用索引运算符被认为是不好的风格?

Why is it considered bad style to use the index operator on a vector in C++?

我正在开发一个使用向量的程序。所以我做的第一件事就是声明我的向量。

std::vector<double> x;
x.reserve(10)

(顺便说一句,这也被认为是不好的做法吗?我应该只输入 std::vector<double> x(10) 吗?)

然后我开始为向量赋值,并询问它的大小。

for (int i=0; i<10; i++)
{
    x[i]=7.1;
}
std::cout<<x.size()<<std::endl;

我不知道它会 return 0,所以经过一些搜索我发现我需要使用 push_back 方法而不是索引运算符。

for (int i=0; i<10; i++)
{
    x.push_back(7.1);
}
std::cout<<x.size()<<std::endl;

现在 returns 10

所以我想知道的是为什么索引运算符允许我在给定索引处访问向量 x 中的值 "stored",但不会更改其大小。另外,为什么这是不好的做法?

当您执行 x.reserve(10) 时,您仅将 capacity 设置为十个元素,但 size 仍为零。

这意味着您在循环中使用索引运算符会越界(因为大小为零)并且您将有 未定义的行为.

如果要设置大小,请使用 resize 或在构造向量时简单地告诉它:

std::vector<double> x(10);

至于向量的容量,当你设置它时(使用例如reserve)然后它分配(在你的情况下)十个元素所需的内存.这意味着当您执行 push_back 时,将不会重新分配矢量数据。

如果不改变容量,或者添加超出容量的元素,那么每次push_back都可能导致向量数据的重新分配。

std::vector<double> x;
x.reserve(10)

BTW, is this also considered bad practice?

不,创建一个空向量并保留内存并不是一个坏习惯。

Should I just type std::vector<double> (10)?)

如果您打算初始化包含 10 个元素的向量,而不是一个空元素,那么是的,您应该这样做。 (如果你的意图是创建一个空向量,则否)

Then I proceeded to assign values to the vector, and ask for its size.

for (int i=0; i<10; i++)
{
    x[i]=7.1;

这有未定义的行为。不要尝试访问不存在的对象。

so after some searching I found out that I needed to use the push_back method instead of the index operator.

这是一种选择。另一种是使用构造函数来初始化元素:std::vector<double> (10)。另一种是使用 std::vector::resize.

Why is it considered bad style to use the index operator on a vector in C++?

不是一般的。如果您尝试访问的索引处没有元素,这是错误的(不仅仅是糟糕的风格)。

Should I just type std::vector<double> x(10)?

当然可以!

中所述std::vector::reserve()仅影响分配策略,但不影响向量的大小。

 std::vector<double> x(10);

实际上等同于

 std::vector<double> x;
 x.resize(10);

std::vector 的括号运算符允许您访问向量中索引 i 处的项目。如果项目 i 不存在,则无法访问,既不能写入也不能读取。

So what I want to know is why the index operator lets me access the value "stored" in vector x at a given index, but wont change its size.

因为它不是为那样工作而设计的。可能设计者并不认为这种行为是可取的。

另请注意,std::vector::reserve 确实为向量保留了内存,但实际上并没有改变它的大小。因此,在调用 x.reserve(10) 之后,尽管已分配了 10 个元素的内部内存,但您的向量的大小仍然为 0。如果现在要添加元素,则不能使用括号运算符,而应使用 std::vector::push_back。此函数会将向量的大小增加一,然后附加您的项目。调用reserve的好处是多次调用push_back时,vector的内存不必重新分配。

std::vector<double> x;
x.reserve(3);
x.push_back(3);
x.push_back(1);
x.push_back(7);

我认为使用 std::vector::resize 可以实现您想要的行为。此函数保留内存 reserve,然后实际更改向量的大小。

std::vector<double> x;
x.resize(3);
x[0] = 3;
x[1] = 1;
x[2] = 7;

前面的代码等同于:

std::vector<double> x(3);
x[0] = 3;
x[1] = 1;
x[2] = 7;

这里的大小是构造函数的参数。以这种方式创建 vector 会在创建时执行调整大小操作。

听起来你在问为什么事情是这样的。大部分取决于效率。

如果 x[i] 要创造尚不存在的价值,那么效率将受到两次打击。首先,索引操作的调用者应确保索引不超过当前向量的大小。其次,即使您要为其分配新值,新元素也需要默认构造。

同时拥有 reserveresize 的原因是相似的。 resize 需要每个元素的默认构造。对于像 vector<double> 这样的东西来说,这似乎没什么大不了的,但是对于 vector<ComplicatedClass> 来说,这可能确实是一件大事。使用 reserve 是一种优化,完全可选,它允许您预测向量的最终大小并防止在它增长时重新分配。

push_back 避免了元素的默认构造,因为内容是已知的,它可以使用移动或复制构造函数。

None 这是错误的样式,请使用适合您情况的任何样式。