4
votes

I am using all the time the same std::vector<int> in order to try to avoid allocating an deallocating all the time. In a few lines, my code is as follows:

std::vector<int> myVector;
myVector.reserve(4);

for (int i = 0; i < 100; ++i) {
    fillVector(myVector);
    //use of myVector
    //....
    myVector.resize(0);
}

In each for iteration, myVector will be filled with up to 4 elements. In order to make efficient code, I want to use always myVector. However, in myVector.resize() the elements in myVector are being destroyed. I understand that myVector.clear() will have the same effect.

I think if I could just overwrite the existing elements in myVector I could save some time. However I think the std::vector is not capable of doing this.

Is there any way of doing this? Does it make sense to create a home-grown implementation which overwrites elements ?

5
What exactly do you mean by "destroyed"? Why is it problem? The space for these elements should remain allocated in the vector...Messa
From std::vector reference: "If n is smaller than the current container size, the content is reduced to its first n elements, removing those beyond (and destroying them)." Therefore, resizing will keep the memory allocated in the vector but the destructor of all the elements in the vector will be called. I think it is enough to change the values in the elements of the vector so no need to call constructors in push_back() and destructors when rezising.Javi
Make sure you only store POD types in your vector, and their destruction will be a no-op.user4815162342
@JaviV How do you deduce from the statement you quoted that resizing will keep the memory allocated in the vector?qdii
@qdii I have been checking clear() vs resizing(0) and they are pretty much the same for this case. When resizing to a smaller siz (or clearing) elements in the vector are destroyed but capacity of the vector does not change. Therefore, push_back o emplace_back operations do not need to reallocate memory.Javi

5 Answers

5
votes

Your code is already valid (myVector.clear() has better style than myVector.resize(0) though).

'int destructor' does nothing.
So resize(0) just sets the size to 0, capacity is untouched.

4
votes

Simply don't keep resizing myVector. Instead, initialise it with 4 elements (with std::vector<int> myVector(4)) and just assign to the elements instead (e.g. myVector[0] = 5).

However, if it's always going to be fixed size, then you might prefer to use a std::array<int, 4>.

4
votes

Resizing a vector to 0 will not reduce its capacity and, since your element type is int, there are no destructors to run:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v{1,2,3};
    std::cout << v.capacity() << ' ';
    v.resize(0);
    std::cout << v.capacity() << '\n';
}

// Output: 3 3

Therefore, your code already performs mostly optimally; the only further optimisation you could make would be to avoid the resize entirely, thereby losing the internal "set size to 0" inside std::vector that likely comes down to an if statement and a data member value change.

0
votes

In fact what you have at present is

for (int i = 0; i < 100; ++i) {
    myVector.reserve(4);
    //use of myVector
    //....
    myVector.resize(0);
}

I do not see any sense in that code.

Of course it would be better to use myVector.clear() instead of myVector.resize(0);

If you always overwrite exactly 4 elements of the vector inside the loop then you could use

std::vector<int> myVector( 4 );

instead of

std::vector<int> myVector;
myVector.reserve(4);

provided that function fillVector(myVector); uses the subscript operator to access these 4 elements of the vector instead of member function push_back

Otherwise use clear as it was early suggested.

0
votes

std::vector is not a solution in this case. You don't want to resize/clear/(de)allocate all over again? Don't.

  1. fillVector() fills 'vector' with number of elements known in each iteration.
  2. Vector is internally represented as continuous block of memory of type T*.
  3. You don't want to (de)allocate memory each time.

Ok. Use simple struct:

struct upTo4ElemVectorOfInts
{
  int data[4];
  size_t elems_num;
};

And modify fillVector() to save additional info:

void fillVector(upTo4ElemVectorOfInts& vec)
{
  //fill vec.data with values
  vec.elems_num = filled_num; //save how many values was filled in this iteration
}

Use it in the very same way:

upTo4ElemVectorOfInts myVector;

for (int i = 0; i < 100; ++i)
{
  fillVector(myVector);
  //use of myVector:
  //- myVector.data contains data (it's equivalent of std::vector<>::data())
  //- myVector.elems_num will tell you how many numbers you should care about
  //nothing needs to be resized/cleared
}

Additional Note:

If you want more general solution (to operate on any type or size), you can, of course, use templates:

template <class T, size_t Size>
struct upToSizeElemVectorOfTs
{
  T data[Size];
  size_t elems_num;
};

and adjust fillVector() to accept template instead of known type.

This solution is probably the fastest one. You can think: "Hey, and if I want to fill up to 100 elements? 1000? 10000? What then? 10000-elem array will consume a lot of storage!". It would consume anyway. Vector is resizing itself automatically and this reallocs are out of your control and thus can be very inefficient. If your array is reasonably small and you can predict max required size, always use fixed-size storage created on local stack. It's faster, more efficient and simpler. Of course this won't work for arrays of 1.000.000 elements (you would get Stack Overflow in this case).