views:

1741

answers:

12

How does container object like vector in stl get destroyed even though they are created in heap?

EDIT

If the container holds pointers then how to destroy those pointer objects

A: 

As with any other object in the heap, it must be destroyed manually (with delete).

zvrba
+3  A: 

When a vector goes out of scope, the compiler issues a call to its destructor which in turn frees the allocated memory on the heap.

Mehrdad Afshari
+1  A: 

This is somewhat of a misnomer. A vector, as with most STL containers, consists of 2 logical parts.

  1. the vector instance
  2. the actual underlying array implementation

While configurable, #2 almost always lives on the heap. #1 however can live on either the stack or heap, it just depends on how it's allocated. For instance

void foo() { 
  vector<int> v;
  v.push_back(42);
}

In this case part #1 lives on the stack.

Now how does #2 get destroyed? When a the first part of a vector is destroyed it will destroy the second part as well. This is done by deleting the underlying array inside the destructor of the vector class.

JaredPar
A: 

To answer your first question:

There's nothing special about STL classes (I hope). They function exactly like other template classes. Thus, they are not automatically destroyed if allocated on the heap, because C++ has no garbage collection on them (unless you tell it to with some fancy autoptr business or something). If you allocate it on the stack (without new) it will most likely be managed by C++ automatically.

For your second question, here's a very simple ArrayOfTen class to demonstrate the basics of typical memory management in C++:

/* Holds ten Objects. */
class ArrayOfTen {
    public:
        ArrayOfTen() {
            m_data = new Object[10];
        }

        ~ArrayOfTen() {
            delete[] m_data;
        }

        Object &operator[](int index) {
            /* TODO Range checking */
            return m_data[index];
        }

    private:
        Object *m_data;

        ArrayOfTen &operator=(const ArrayOfTen &) { }
};

ArrayOfTen myArray;
myArray[0] = Object("hello world"); // bleh

Basically, the ArrayOfTen class keeps an internal array of ten Object elements on the heap. When new[] is called in the constructor, space for ten Objects is allocated on the heap, and ten Objects are constructed. Simiarly, when delete[] is called in the destructor, the ten Objects are deconstructed and then the memory previously allocated is freed.

For most (all?) STL types, resizing is done behind the scenes to make sure there's enough memory set asside to fit your elements. The above class only supports arrays of ten Objects. It's basically a very limiting typedef of Object[10].

strager
+9  A: 

An STL container of pointer will NOT clean up the data pointed at. It will only clean up the space holding the pointer. If you want the vector to clean up pointer data you need to use some kind of smart pointer implementation:

{
    std::vector<SomeClass*> v1;
    v1.push_back(new SomeClass());

    std::vector<boost::shared_ptr<SomeClass> > v2;
    boost::shared_ptr<SomeClass> obj(new SomeClass);
    v2.push_back(obj);
}

When that scope ends both vectors will free their internal arrays. v1 will leak the SomeClass that was created since only the pointer to it is in the array. v2 will not leak any data.

jmucchiello
+4  A: 

If you have a vector<T*>, your code needs to delete those pointers before delete'ing the vector: otherwise, that memory is leaked.

Know that C++ doesn't do garbage collection, here is an example of why (appologies for syntax errors, it has been a while since I've written C++):

typedef vector<T*> vt;
⋮
vt *vt1 = new vt, *vt2 = new vt;
T* t = new T;
vt1.push_back(t);
vt2.push_back(t);
⋮
delete vt1;

The last line (delete vt1;) clearly should not delete the pointer it contains; after all, it's also in vt2. So it doesn't. And neither will the delete of vt2.

(If you want a vector type that deletes pointers on destroy, such a type can of course be written. Probably has been. But beware of delete'ing pointers that someone else is still holding a copy of.)

derobert
Yes it has: boost::ptr_vector. (see boost ptr containers)
Martin York
+3  A: 

If you store pointers in STL container classes you need to manually delete them before the object gets destroyed. This can be done by looping through the whole container and deleting each item, or by using some kind of smart pointer class. However do not use auto_ptr as that just does not work with containers at all.

A good side effect of this is that you can keep multiple containers of pointers in your program but only have those objects owned by one of those containers, and you only need to clean up that one container.

The easiest way to delete the pointers would be to do:

for (ContainerType::iterator it(container.begin()); it != container.end(); ++it)
{
    delete (*it);
}
Daemin
This is not advisable (unless part of the destructor) as it is not exception safe. You should use a container that understands that is holding a pointer.
Martin York
How is it not exception safe? If ContainerType is something like std::vector<std::string*> for instance then this is perfectly valid code to put in both a destructor and a "clear" or "reset" like method.
Daemin
+2  A: 

Use either smart pointers inside of the vector, or use boost's ptr_vector. It will automatically free up the allocated objects inside of it. There are also maps, sets, etc.

http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_vector.html and the main site: http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_container.html

Michel
A: 

To delete the elements pointed at, I wrote a simple functor:

template<typename T>
struct Delete {
    void operator()( T* p ) const { delete p; }
};

std::vector< MyType > v;
// ....
std::for_each( v.begin(), v.end(), Delete<MyType>() );

But you should fallback on shared pointers when the vector's contents are to be ... ehm... shared. Yes.

xtofl
A: 

The standard STL containers place a copy of the original object into the container, using the copy constructor. When the container is destroyed the destructor of each object in the container is also called to safely destroy the object.

Pointers are handled the same way.
The thing is pointers are POD data. The copy constructor for a pointer is just to copy the address and POD data has no destructor. If you want the container to manage a pointer you need to:

  • Use a container of smart pointers. (eg shared pointer).
  • Use a boost ptr container.

I prefer the pointer container:
The pointer containers are the same as the STL containers except you put pointers into them, but the container then takes ownership of the object the pointer points at and will thus deallocate the object (usually by calling delete) when the container is destroyed.

When you access members of a ptr container they are returned via reference so they behave just like a standard container for use in the standard algorithms.

int main()
{
    boost::ptr_vector<int>    data;

    data.push_back(new int(5));
    data.push_back(new int(6));

    std::cout << data[0] << "\n";  // Prints 5.
    std::cout << data[1] << "\n";  // Prints 6.


}   // data deallocated.
    // This will also de-allocate all pointers that it contains.
    // by calling delete on the pointers. Therefore this will not leak.

One should also point out that smart pointers in a container is a valid alternative, unfortunately std::auto_ptr<> is not a valid choice of smart pointer for this situation.

This is because the STL containers assume that the objects they contain are copyable, unfortunately std::auto_ptr<> is not copyable in the traditional sense as it destroys the original value on copy and thus the source of the copy can not be const.

Martin York
A: 

STL containers are like any other objects, if you instantiate one it is created on the stack:

std::vector<int> vec(10);

Just like any other stack variable, it only lives in the scope of the function it is defined in, and doesn't need to be manually deleted. The destructor of STL containers will call the destructor of all elements in the container.

Keeping pointers in a container is a dicey issue. Since pointers don't have destructors, I would say you would never want to put raw pointers into an STL container. Doing this in an exception safe way will be very difficult, you'd have to litter your code with try{}finally{} blocks to ensure that the contained pointers are always deallocated.

So what should you put into containers instead of raw pointers? +1 jmucchiello for bringing up boost::shared_ptr. boost::shared_ptr is safe to use in STL containers (unlike std::auto_ptr). It uses a simple reference counting mechanism, and is safe to use for data structures that don't contain cycles.

What would you need for data structures that contain cycles? In that case you probably want to graduate to garbage collection, which essentially means using a different language like Java. But that's another discussion. ;)

Anton I. Sipos