Resizing an "std::vector"; which elements are affected? - c++

std::vector<AClass> vect;
AClass Object0, Object1, Object2, Object3, Object4;
vect.push_back(Object0); // 0th
vect.push_back(Object1); // 1st
vect.push_back(Object2); // 2nd
vect.push_back(Object3); // 3rd
vect.push_back(Object4); // 4th
Question 1 (Shrinking): Is it guarantied that the 0th, 1st and 2nd elements are protected (i.e.; their values do not change) after resizing this vector with this code: vect.resize(3)?
Question 2 (Expanding): After expanded this vector by the code vect.resize(7);
a. Are the first 5 elements (0th through 4th) kept unchanged?
b. What happens to the newly added two elements (5th and 6th)? What are their default values?

Question 1: Yes, the standard says:
void resize(size_type sz);
If sz < size(), equivalent to erase(begin() + sz, end());.
Question 2: If no resizing is required, yes. Otherwise, your elements will be copied to a different place in memory. Their values will remain unchanged, but those values will be stored somewhere else. All iterators, pointers and references to those objects will be invalidated. The default value is AClass().

Question 1:
Yes, from cplusplus.com "If sz is smaller than the current vector size, the content is reduced to its first sz elements, the rest being dropped."
Question 2:
a) The first elements are kept unchanged, the vector just increases the size of it's internal buffer to add the new elements.
b) The default constructor of AClass is called for the insertion of each new element.

vector always grows and shrinks at the end, so if you reduce the size of a vector only the last elements are removed. If you grow a vector with resize, new elements are added onto the end using the a default-constructed object as the value for the new entries. For a class, this is the value of a new object created with the default constructormas. For a primitive, this is zero (or false for bool).
And yes, elements that aren't removed are always protected during a resize.

Yes, when you shrink a vector, all the objects that remain retain their prior values.
When you expand a vector, you supply a parameter specifying a value that will be used to fill the new slots. That parameter defaults to T().

Related

What does the second parameter in std::vector.resize() mean?

I'm having some problems understanding what const value_type& val means in this function:
void std::vector::resize (size_type n, const value_type& val);
I read the C++ reference and here's what it says:
Object whose content is copied to the added elements in case that n is greater than the current container size.
If not specified, the default constructor is used instead.
Member type value_type is the type of the elements in the container, defined in vector as an alias of the first template parameter (T).
But what exactly does it do? For example I have this line of code:
myVector.resize(10, numeric_limits<double>::infinity());
The content of infinity is copied to myVector if 10 is bigger than its current size?
If someone could please explain, that would be great.
Thank you!
It sets all the values in the vector to that value, so if myVector is empty to begin with, it would contain 10 doubles, with infinity as the value. If you already have the values 1.3, 2.9, 3.6, -1.9 and 5.2 in the vector, the next five elements will be infinity.
Obviously, it makes no difference if you have a value there or what the value is, if the vector was already bigger than 10 elements.
Yes. The resize() function can be used to increase or decrease the size of the vector. However, if you use it to increse the size (if the new size is greater than the current size), something has to go in those new cells. If you don't specify the second parameter then this value is the default constructor for whatever data type the vector holds; however, you can use the second parameter to specify what that default value should be. Common uses of this are to specify a value of 0, NULL, etc.
Despite some elaborate language, it's pretty simple. If the resize creates new elements, then of course they have to be initialized with something. Normally, for a vector<T>, they will be initialized by invoking the default constructor of T. Providing that second argument will instead initialize the new elements to copies of that value.
In your example, indeed, the call ensures that the vector has 10 items, and if it had less than that, all new items receive a copy of infinity. So if your vector had 6 elements, it will get 4 new copies of infinity.

Vector Resize vs Reserve for nested vectors

I'm trying to use another project's code and they have structs of this form:
struct data{
std::vector<sparse_array> cols,rows;
}
struct sparse_array {
std::vector<unsigned int> idxs;
std::vector<double> values;
void add(unsigned int idx, double value) {
idxs.push_back(idx);
values.push_back(value);
}
}
For my code, I tried using the following lines:
data prob;
prob.cols.reserve(num_cols);
prob.rows.reserve(num_rows);
// Some loop that calls
prob.cols[i].add(idx, value);
prob.rows[i].add(idx, value);
And when I output the values, prob.rows[i].value[j] to a file I get all zeros. But when I use resize instead of reserve I get the actual value that I read in. Can someone give me an explanation about this?
Function reserve() simply allocates a contiguous region of memory big enough to hold the number of items you specify and moves the vector's old content into this new block, which makes sure no more reallocations for the vectors' storage will be done upon insertions as long as the specified capacity is not exceeded. This function is used to reduce the number of reallocations (which also invalidate iterators), but does not insert any new items at the end of your vector.
From the C++11 Standard, Paragraph 23.3.6.3/1 about reserve():
A directive that informs a vector of a planned change in size, so that it can manage the storage
allocation accordingly. After reserve(), capacity() is greater or equal to the argument of reserve if reallocation happens; and equal to the previous value of capacity() otherwise. Reallocation happens at this point if and only if the current capacity is less than the argument of reserve(). If an exception is thrown other than by the move constructor of a non-CopyInsertable type, there are no effects.
Notice that by doing prob.cols[i].push_back(idx, value); you are likely to get undefined behavior, since i is probably an out-of-bounds index.
On the other hand, function resize() does insert items at the end of your vector, so that the final size of the vector will be the one you specified (this means it can even erase elements, if you specify a size smaller than the current one). If you specify no second argument to a call to resize(), the newly inserted items will be value-initialized. Otherwise, they will be copy-initialized from the value you provide.
From the C++11 Standard, Paragraph 23.3.6.3/9 about resize():
If sz <= size(), equivalent to erase(begin() + sz, end());. If size() < sz, appends
sz - size() value-initialized elements to the sequence.
So to sum it up, the reason why accessing your vector after invoking resize() gives the expected result is that items are actually being added to the vector. On the other hand, since the call to reserve() does not add any item, subsequent accesses to non-existing elements will give you undefined behavior.
If the vector is empty, then std::vector::resize(n) expands the content of this vector by inserting n new elements at the end. std::vector::reserve(n) only reallocates the memory block that your vector uses for storing its elements so that it's big enough to hold n elements.
Then when you call prob.cols[i], you are trying to access the element at index i. In case you used reserve before, this results in accessing the memory where no element resides yet, which produces the undefined behavior.
So just use resize in this case :)

Are vector elements guaranteed to be in order?

I understand that having pointers to elements of a vector is a bad idea, because upon expanding, the memory addresses involved will change, therefore invalidating the pointer(s). However, what if I were to simply use an integer that holds the index number of the element I want to access? Would that be invalidated as the vector grows in size? What I am thinking of looks something like this:
#include <vector>
class someClass{
string name
public: string getName(){return name;}
};
vector<someClass> vObj;
int currIdx;
string search;
cout<<"Enter name: ";
cin>>search;
for(int i=0; i<vObj.size(); i++){
if(vObj[i].getName()==search)
currIdx = i;}
No, the index numbers are of course not invalidated when the vector expands. They are invalidated (in the sense that you no longer find the same elements at a constant index) if you erase a previous element, though:
vector: 3 5 1 6 7 4
Here, vector[2] == 1. But if you erase vector[1] (the 5), then afterwards, vector[2] == 6.
I think the title of your question and what you seem to be asking do not really match. No vector is by definition guaranteed to be sorted, so elements won't be "in order".
Moreover, all iterators and references to elements of a vector will be invalidated upon insertion only if reallocation occurs (i.e. when the size of the vector exceeds its capacity). Otherwise, iterators and references before the point of insertion will not be invalidated (see Paragraph 23.3.6.5/1 of the C++11 Standard).
Storing an index is only subject to a potential logical invalidation: if you insert elements into the vector in a position prior to the one you are indexing, the element you were indexing will be shifted one position to the right, and the same index will now refer to a different element ; likewise, if you erase an element prior to the position you were indexing, the element you were indexing will be shifted on position to the left - and your index may now refer to a position which is out-of-bounds.
No, the index numbers are not invalidated when the vector is expanded. Since you're declaring that the vector container object is not a pointer vector<someClass> instead of vector<someClass*>, your pointed to element will be preserved as well.
It shouldn't, as the system will simply allocate more memory and then do a memcopy.
Order should be preserved in the std::vector STL template.
And yes, if you delete elements the ordering will change. But if you are going to be doing a lot of deletes, use a different data structure such as a linked list.

What happens when I resize this vector (of vectors)

std::vector<std::vector<int>> vecOfVecs;
vecOfVecs.resize(10);
What will be positions 0-9 of vecOfVecs? Instances of std::vector?
If so, is this legitimate:
std::vector<int> *pToVec = &(vecOfVecs[0]);
pToVec->push_back(10);
Yes, positions 0-9 will be empty instances of std::vector<int>, exactly as if you had said
for (size_t i = vecOfVecs.size(); i < 10; ++i) {
vecOfVecs.push_back(std::vector<int>());
}
The "canonical" definition of std::vector::resize in C++03 actually has two parameters, not one, with the second argument being the element value that is used as a "fill value" for the freshly created elements. The second parameter has a default argument equal to a value-initialized object of element type. This means that your call
vecOfVecs.resize(10);
is actually translated into a
vecOfVecs.resize(10, std::vector<int>());
call. I.e. it is actually you who implicitly supplied a default-constructed instance of std::vector<int> to be used as an initializer for all new elements.
Your pToVec->push_back(10) call is perfectly legitimate.
C++11 made some insignificant (in this context) changes to the definition of resize, but the general effect remains the same: the new elements are value-initialized for you. They are ready to be used immediately after the call to resize.
There is a default second parameter of resize that tells what value you'd like to insert into the additional space. If that value is not specified, the default-constructed value is used instead.
void resize (size_type n, value_type val = value_type());
If n is greater than the current container size, the content is expanded by inserting at the end as many elements as needed to reach a size of n. If val is specified, the new elements are initialized as copies of val, otherwise, they are value-initialized.
Your example is entirely legitimate: your vector of vectors will contain ten empty vectors.

std::vector elements initializing

std::vector<int> v1(1000);
std::vector<std::vector<int>> v2(1000);
std::vector<std::vector<int>::const_iterator> v3(1000);
How elements of these 3 vectors initialized?
About int, I test it and I saw that all elements become 0. Is this standard? I believed that primitives remain undefined. I create a vector with 300000000 elements, give non-zero values, delete it and recreate it, to avoid OS memory clear for data safety. Elements of recreated vector were 0 too.
What about iterator? Is there a initial value (0) for default constructor or initial value remains undefined? When I check this, iterators point to 0, but this can be OS
When I create a special object to track constructors, I saw that for first object, vector run the default constructor and for all others it run the copy constructor. Is this standard?
Is there a way to completely avoid initialization of elements? Or I must create my own vector? (Oh my God, I always say NOT ANOTHER VECTOR IMPLEMENTATION)
I ask because I use ultra huge sparse matrices with parallel processing, so I cannot use push_back() and of course I don't want useless initialization, when later I will change the value.
You are using this constructor (for std::vector<>):
explicit vector (size_type n, const T& value= T(), const Allocator& = Allocator());
Which has the following documentation:
Repetitive sequence constructor: Initializes the vector with its content set to a repetition, n times, of copies of value.
Since you do not specify the value it takes the default-value of the parameter, T(), which is int in your case, so all elements will be 0
They are default initialized.
About int, I test it and I saw that all elements become 0. Is this standard? I believed that primitives remain undefined.
No, an uninitialized int has an indeterminate value. These are default initialized, i.e.,
int i; // uninitialized, indeterminate value
int k = int(); // default initialized, value == 0
In C++11 the specification for the constructor vector::vector(size_type n) says that n elements are default-inserted. This is being defined as an element initialized by the expression allocator_traits<Allocator>::construct(m, p) (where m is of the allocator type and p a pointer to the type stored in the container). For the default allocator this expression is ::new (static_cast<void*>(p)) T() (see 20.6.8.2). This value-initializes each element.
The elements of a vector are default initialized, which in the case of POD types means zero initialized. There's no way to avoid it with a standard vector.