Reputation: 328
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?
Upvotes: 4
Views: 4107
Reputation: 308520
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.
Upvotes: 4
Reputation: 4092
The bracket operator of the std::vector
lets you access an item at the index i
in your vector. If an item i
does not exist, it cannot be accessed, neither for writing nor for reading.
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.
Because it wasn't designed to work that way. Probably the designers did not think that this behaviour would be desirable.
Please also note that std::vector::reserve
does reserve memory for the vector but does not actually change its size. So after calling x.reserve(10)
your vector has still got a size of 0
although internally memory for 10 elements has been allocated. If you now want to add an element, you must not use the bracket operator but std::vector::push_back
instead. This function will increase the vector's size by one and then append your item. The advantage of calling reserve
is that the memory for the vector must not be reallocated when calling push_back
multiple times.
std::vector<double> x;
x.reserve(3);
x.push_back(3);
x.push_back(1);
x.push_back(7);
I think the behaviour you desire could be achieved using std::vector::resize
. This function reserves the memory as reserve
would and then actually changes the size of the vector.
std::vector<double> x;
x.resize(3);
x[0] = 3;
x[1] = 1;
x[2] = 7;
The previous code is equivalent to:
std::vector<double> x(3);
x[0] = 3;
x[1] = 1;
x[2] = 7;
Here the size is the constructor argument. Creating the vector
this way performs the resize operation on creation.
Upvotes: 1
Reputation: 1
Should I just type
std::vector<double> x(10)
?
Definitely yes!
As mentioned in @Some programmer dude's answer std::vector::reserve()
only affects allocation policies but not the size of the vector.
std::vector<double> x(10);
is actually equivalent to
std::vector<double> x;
x.resize(10);
Upvotes: 1
Reputation: 238441
std::vector<double> x; x.reserve(10)
BTW, is this also considered bad practice?
No, creating an empty vector and reserving memory is not a bad practice.
Should I just type
std::vector<double> (10)
?)
If your intention is to initialize the vector of 10 elements, rather than empty one, then yes you should. (If your intention is to create an empty vector, then no)
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;
This has undefined behaviour. Do not try to access objects that do not exist.
so after some searching I found out that I needed to use the push_back method instead of the index operator.
That is one option. Another is to use the constructor to initialize the elements: std::vector<double> (10)
. Yet another is to use std::vector::resize
.
Why is it considered bad style to use the index operator on a vector in C++?
It is not in general. It is wrong (not just bad style) if there are no elements at the index that you try to access.
Upvotes: 2
Reputation: 409442
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.
Upvotes: 5