Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I am working on a program that uses vectors. So the first thing I did was declare my vector.

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

(BTW, is this also considered bad practice? Should I just type std::vector<double> x(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;
}
std::cout<<x.size()<<std::endl;

I didn't know it would return 0, so after some searching I found out that I needed to use the push_back method instead of the index operator.

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

And now it returns 10.

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. Also, why is this bad practice?

like image 984
Nerdrigo Avatar asked Dec 08 '22 19:12

Nerdrigo


2 Answers

When you do x.reserve(10) you only set the capacity to ten elements, but the size is still zero.

That means then you use the index operator in your loop you will go out of bounds (since the size is zero) and you will have undefined behavior.

If you want to set the size, then use either resize or simply tell it when constructing the vector:

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

As for the capacity of the vector, when you set it (using e.g. reserve) then it allocates the memory needed for (in your case) ten elements. That means when you do push_back there will be no reallocations of the vector data.

If you do not change the capacity, or add elements beyond the capacity, then each push_back may cause a reallocation of the vector data.

like image 111
Some programmer dude Avatar answered May 01 '23 21:05

Some programmer dude


It sounds like you're asking why things are the way they are. Most of it is down to efficiency.

If x[i] were to create value if it didn't already exist, there would be two hits to efficiency. First, the caller of indexing operations should ensure the index is not beyond the current size of the vector. Second, the new element would need to be default constructed even if you're about to assign a new value into it anyway.

The reason for having both reserve and resize is similar. resize requires a default construction of every element. For something like vector<double> that doesn't seem like a big deal, but for vector<ComplicatedClass>, it could be a big deal indeed. Using reserve is an optimization, completely optional, that allows you to anticipate the final size of the vector and prevent reallocations while it grows.

push_back avoids the default construction of an element, since the contents are known, it can use a move or copy constructor.

None of this is the wrong style, use whatever's appropriate for your situation.

like image 21
Mark Ransom Avatar answered May 01 '23 21:05

Mark Ransom