4

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.
Jens Åkerblom
  • 898
  • 8
  • 19
  • How about `std::shared_ptr` ? – iammilind Mar 14 '12 at 06:07
  • 1
    Are you open to using C++11 features or Boost? – Emile Cormier Mar 14 '12 at 06:13
  • I just started using Boost (1.49.0) but the auto_ptr is defined in std (memory). The boost::shared_ptr works however, since the actual ownership should be held by the container there's some overhead due to reference counting. – Jens Åkerblom Mar 14 '12 at 06:17
  • 3
    Why do you care if it is copied in? Is it very expensive to copy? – Martin York Mar 14 '12 at 06:18
  • This is of course an abstract question, but for the sake of the argument yes; it's expensive to copy. If using something as simple as integers then I would of course use the first option listed in question. – Jens Åkerblom Mar 14 '12 at 06:25
  • 1
    Do you also care about the unnecessary copies that could occur in the vector's internals, for example upon resizing or sorting? – juanchopanza Mar 14 '12 at 06:40
  • Are copies made when resizing and sorting? I rarelly sort (use priority queues instead) and when resizing, are you refering to the resize method? – Jens Åkerblom Mar 14 '12 at 07:17
  • @JensÅkerblom: obviously in your example since two objects are constructed, two will ultimately be destroyed, and thus one destructor will be called at the end of the expression (for the temporary) and one when the vector gets rid of the object it holds. – Matthieu M. Mar 14 '12 at 07:21
  • Not necessarily resize(), but say your vector reaches maximum capacity and you insert a new element. The vector has to copy everything in a new, larger section in memory. If you have c++11 you can implement move construction and assignment in your class, and/or use Xeo's answer with unique_ptr. – juanchopanza Mar 14 '12 at 07:22

5 Answers5

4

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.

Emile Cormier
  • 28,391
  • 15
  • 94
  • 122
3

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.

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • 3
    `emplace_back` is also (partially) supported in C++03 by [Boost.Container](http://www.boost.org/doc/libs/release/doc/html/container/move_emplace.html#container.move_emplace.emplace). – Mankarse Mar 14 '12 at 06:19
  • This seems like the best way to go to avoid copy construction when containers own the objects. – Jens Åkerblom Mar 14 '12 at 06:38
  • @Mankarse: How it is possible in C++03 without rvalue references? – Jesse Good Mar 14 '12 at 06:38
  • @Jesse: Note the "partially " (;. Boost.Container uses the rvalue reference emulation provided by Boost.Move. The arguments are always forwarded by either const reference or emulated rvalue, so the argument forwarding is not as perfect as that allowed by C++11. – Mankarse Mar 14 '12 at 06:50
2

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.

Community
  • 1
  • 1
Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • Note commonly one would want to go for pointers as containers members better performance, but unless you **profile** and determine that *value semantics* is a problem doing so is premature optimization. – Alok Save Mar 14 '12 at 06:23
1

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.
Martin York
  • 257,169
  • 86
  • 333
  • 562
0

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(...));
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454