Note: In this article, we will use vectors for demonstration. But the same applies for all collections in C++.
Have you wondered, how C++ STL stores objects in its storage and its consequences? That is exactly what we are going to discuss today. When you insert, push_back, assign, etc an object into C++ STL collections, the default behavior is C++ copies the object using copy constructor and stores the newly created object. One obvious consequence of this is that, changes made to the original object inserted has no effect on the object stored.
Original before modification: 5
Original after modification: 6
Stored after modification: 5
Another consequence and the most important consequence occurs when the class has dynamically allocated members. The default copy constructor provided by C++ does a shallow copy of these pointers. This will lead to segmentation faults when the objects are being destructed.
copy constructor Data(const Data &d)
*** Error in `./copy_problems': double free or corruption (fasttop): 0x00000000021f4010 ***
Aborted (core dumped)
So, how do we overcome this issue? Below we will discuss various ways in which this problem can be fixed.
Vector of pointers to objects
One of the easiest way to achieve this is by creating a vector of pointers to objects instead of vector of objects. This makes sure that the object stored is the object inserted.
It should be noted that automatic variables created on stack should not be stored this way, if the vector could be passed out of the function. This is because when the function frame is unrolled, the object allocated on the stack is destroyed.
Note: In C++, vector of reference to objects is not possible.
Vector of shared_ptr to objects
As you might have noticed, the previous solution has two problems:
- Objects should be allocated in heap
- The destruction of the object is left to the developer. This could lead to serious problems!
The obvious solution to fix the problems with previous solution is to use shared_ptr instead of raw pointers. shared_ptr keeps reference counts and destructs the object when there are no references left to the object. This works well because methods that remove elements from vector calls the destructor of the element being removed.
Use move semantics instead of copy semantics
C++ STL collections allow the use of move semantics instead of copy semantics to clone the object being inserted. I will dedicate an entire post for this solution.