Jens Åkerblom
Jens Åkerblom

Reputation: 908

Element Lifetime of STL Containers

I'm trying to save objects in an stl container (in this case a vector) and want the container to destroy the objects at its destruction, but I can't quite figure out the details.

One way I don't want to do it is simply using it like

vector<MyClass> myVec;
myVec.push_back(MyClass(...));

due to the fact that the constructor here is called twice (once in above code, then copy constructor in vector) and the destructor once.

The most direct alternative is to use pointers to store dynamically allocated objects, but then the destructor of MyClass won't be called at vector destruction. Storing auto_ptr instead of normal pointers gives an error at myVec.push_back(...).

Is there anyway to avoid the first option while having the container's destructor call the elements' destructor?

Thank you for your answers!

EDIT

Consider the similar problem; how to implement a container owning objects using an abstract base class. Unique pointer (Boost's unique_ptr) don't have copy constructors so one can't use that directly.

class A {};             // Abstract base class.
class B : public A {};  // Sub class.

...


vector<A *> vec;
vec.push_back(new B());

// At destruction of vec, destroy elements left in container.

Upvotes: 4

Views: 1496

Answers (5)

Emile Cormier
Emile Cormier

Reputation: 29199

An alternative not already mentioned is the Boost Pointer Container Library.

Boost.Pointer Container provides containers for holding heap-allocated objects in an exception-safe manner and with minimal overhead. The aim of the library is in particular to make OO programming easier in C++ by establishing a standard set of classes, methods and designs for dealing with OO specific problems

With a boost::ptr_vector, instead of pushing back copies, you push pointers to dynamically allocated objects. The ptr_vector takes ownership of these objects and ensures that they are deleted when the ptr_vector itself is deleted. Clients that read from the ptr_vector use the same interface as a regular std::vector, so they don't have to deal with pointers. For example, boost::ptr_vector<T>::front() returns a reference.

The Motivation section of the documentation will help you decide if this is the right solution for you.

Upvotes: 4

Loki Astari
Loki Astari

Reputation: 264331

If you want to store pointers I like boost::ptr_vector

This acts like vector but stores and takes ownership of pointers.

The advantage of boost::ptr_vector<X> over std::vector<some_smart_ptr<X>> is that element access on the ptr_vector returns a reference to the object rather than a reference to a (smart) pointer. This makes using the container with standard algorithms easier (as you do not need to bind a functor to dereference the element).

But unless there is a very good reason it is still best to store the object by value in a normal std::vector.

Good reason may include:

  • Very expensive to copy.
  • Can not use perfect forwarding to construct in place.
  • The container must store polymorphic objects.

Upvotes: 1

Alok Save
Alok Save

Reputation: 206508

The best possible way is to use elements by value in Standard Library containers.
Standard Library containers work on value semantics. i.e: they store elements by value and You know for sure that the container takes the ownership of the elements. So you do not need an explicit manual memory management.

As long as you obey the Rule of Three, the calling of copy constructor and destructor when storing elements by value should not be a problem for you.

In case of pointer as container elements you have to manually manage the memory and explicitly de-allocate the dynamic allocation.
In this case where You need the object to reside in dynamic memory, You should use Smart pointers.
auto_ptr is deprecated and it cannot be used in Standard Library containers because it has a non intutive assignment behavior. unique_ptr is the superior choice to auto_ptr proposed by the new c++11 standard.

Note that which smart pointer to use depends on the lifetime and the ownership semantics of the element, check out the link to know how to choose one for your usage.

Upvotes: 2

JaredPar
JaredPar

Reputation: 754545

It sounds like you want to have a single value which will have a lifetime equivalent of the containing vector<T>. If so this is a good place to consider using std::shared_ptr<T> which is a ref counted pointer type

typedef std::shared_ptr<MyClass> MyClassPtr;
...
vector<MyClassPtr> myVec;
myVec.push_back(new MyClass(...));

Upvotes: 0

Xeo
Xeo

Reputation: 131779

C++11 has emplace_back which will perfectly forward whatever you give it to the elements constructor and construct it directly in-place:

#include <vector>
#include <iostream>

struct X{
  X(int i, float f, bool b){
    std::cout << "X(" << i << ", " << f << ", " << b << ")\n";
  }
};

int main(){
  std::vector<X> vx;
  vx.emplace_back(42, 3.14f, true);
}

Live example on Ideone.

In C++03, you're out of luck and have to live with the copy ctor call (or rather, two - one in the parameter, one into the vector's internal array). If your class is desgined correctly, it should only be a minor efficiency inconvenience.

Upvotes: 3

Related Questions