shrink_to_fit() function and implementation for pre-modern C++

Cengizhan Varlı
4 min readAug 12, 2023

--

First of all we must learned two important terms;

  • Capacity: This refers to the amount of memory allocated to the vector container and which can be used at a particular time. It means that if the particular memory is allocated to the vector, whether it has that number of elements or not, it counts to its capacity.
  • Size: This refers to the actual number of elements present in the vector, which is actually holding some data or elements into it.

Now let’s understand the shrink_to_fit() function. As the name suggests, this function asks the vector container to reduce its capacity to its size. In other words, the capacity will become equal to the number of elements actually present inside the vector container.

Requests the container to reduce its capacity to fit its size.

shrink_to_fit() is a member function in C++'s Standard Library that is used to reduce the capacity of a dynamic array or container to match its current size. It is available for various standard containers like std:.vector , std::string, and std::deque. The purpose of shrink_to_fit() is to release any excess memory that the container might be holding beyond its actual data, resulting in better memory utilization.

We can understand better with follows examples;

 std::vector<int> vec(50'000);
std::cout<<"Size : "<<vec.size()<<" Capacity : "<<vec.capacity()<<"\n";

vec.erase(vec.begin() + 10, vec.end());

std::cout<<"Size : "<<vec.size()<<" Capacity : "<<vec.capacity()<<"\n";
Output

We created a vector with 50000 elements as seen above. Each element is initialized with the default value of int, which is 0.

As seen in the first print process, the size and capacity values ​​are 50000.

vec.erase(vec.begin() + 10, vec.end()); This line erases elements from the vector starting from the iterator at position 10 up to the end of the vector. This means it removes all elements from index 10 onwards. The elements before index 10 remain unchanged.

The size value seen in the last print operation has changed, but the capacity value has not changed. In other words, there is still 5000 capacity reserved for this vector in memory. There is no guarantee that the capacity value will change after the erase operation.

This is an example of bad coding.

Thanks to the shrink_to_fit() function that came into our lives with C++11, we can solve the capacity problems that will occur after deletion operations.

Let’s call the shrink_to_fit() function after the deletion and see the result;

 std::vector<int> vec(50'000);
std::cout<<"Size : "<<vec.size()<<" Capacity : "<<vec.capacity()<<"\n";

vec.erase(vec.begin() + 10, vec.end());

vec.shrink_to_fit();

std::cout<<"Size : "<<vec.size()<<" Capacity : "<<vec.capacity()<<"\n";
Output

As seen above, after the calling shrink_to_fit() function, Size and Capacity values are same.

In fact, there is no guarantee of this according to the standards, but almost all of the compilers provide this to us as above.

We said that this function came into our lives with C++11, So what were we doing before Modern C++ for such situations?

If you need to write code in C++98 like me, you can provide the shrink_to_fit() function by writing a code like below;

  std::vector<int> vec(50'000);
std::cout<<"Size : "<<vec.size()<<" Capacity : "<<vec.capacity()<<"\n";

vec.erase(vec.begin() + 10, vec.end());

//vec.shrink_to_fit();

std::vector<int>(vec).swap(vec);

std::cout<<"Size : "<<vec.size()<<" Capacity : "<<vec.capacity()<<"\n";
Output

In the code, we have made use of the swap technique to reduce the capacity of the vector after erasing elements. This technique effectively swaps the contents of a temporary vector with the original vector and then allows the temporary vector to be destroyed, releasing any excess memory.

The line std::vector<int>(vec).swap(vec); in the given C++ code is performing an operation to efficiently reduce the capacity of the vector vec after elements have been erased from it.

Let’s break down what this line of code does:

  1. std::vector<int>(vec): This part creates a temporary vector by using the constructor of std::vector<int>, which takes another vector (vec in this case) as an argument. This temporary vector is a copy of the original vec vector, including its elements and size, but it has its own separate memory allocation.
  2. .swap(vec): This part of the line calls the swap method on the temporary vector, exchanging its internal memory allocation with the memory allocation of the original vector vec. After the swap, the temporary vector's memory is now used by vec, and the temporary vector itself is destroyed as it goes out of scope.

The purpose of this line is to efficiently reduce the capacity of the vector vec after the elements have been erased using vec.erase(…). The swap operation effectively swaps the memory allocation between the temporary vector and the original vector, allowing the original vector vec to release any excess memory that was allocated but not needed anymore after the erase operation.

By performing this swap, the vector vec ends up with a capacity that closely matches its size, reducing memory wastage. The shrink_to_fit() function could also achieve a similar result, but using swap is an alternative approach that can also be used to achieve the same goal.

This is a common idiom used to reduce the capacity of a vector.

--

--

No responses yet