2
votes

It is the first time I am using STL and I am confused about how should I deallocate the the memory used by these containers. For example:

class X {
    private:
        map<int, int> a;
    public:
        X();
        //some functions
}

Now let us say I define the constructor as:

X::X() {
    for(int i=0; i<10; ++i) {
        map[i]=i;
    }
}

Now my question is should I write the destructor for this class or the default C++ destructor will take care of deallocating the memory(completely)?

Now consider the modification to above class

class X {
    private:
        map<int, int*> a;
    public:
        X();
        ~X();
        //some functions
}

Now let us say I define the constructor as:

X::X() {
    for(int i=0; i<10; ++i) {
        int *k= new int;
        map[i]=k;
    }
}

Now I understand that for such a class I need to write a destructor as the the memory allocated by new cannot be destructed by the default destructor of map container(as it calls destructor of objects which in this case is a pointer). So I attempt to write the following destructor:

X::~X {
    for(int i=0; i<10; ++i) {
        delete(map[i]);
    }
    //to delete the memory occupied by the map.
}

I do not know how to delete the memory occupied by the map. Although clear function is there but it claims to bring down the size of the container to 0 but not necessarily deallocate the memory underneath. Same is the case with vectors too(and I guess other containers in STL but I have not checked them).

Any help appreciated.

5

5 Answers

4
votes

should I write the destructor for this class or the default C++ destructor will take care of deallocating the memory(completely)?

Yes it will. All the standard containers follow the principle of RAII, and manage their own dynamic resources. They will automatically free any memory they allocated when they are destroyed.

I do not know how to delete the memory occupied by the map.

You don't. You must delete something if and only if you created it with new. Most objects have their memory allocated and freed automatically.

The map itself is embedded in the X object being destroyed, so it will be destroyed automatically, and its memory will be freed along with the object's, once the destructor has finished.

Any memory allocated by the map is the responsibility of the map; it will deallocate it in its destructor, which is called automatically.

You are only responsible for deleting the dynamically allocated int objects. Since it is difficult to ensure you delete these correctly, you should always use RAII types (such as smart pointers, or the map itself) to manage memory for you. (For example, you have a memory leak in your constructor if a use of new throws an exception; that's easily fixed by storing objects or smart pointers rather than raw pointers.)

2
votes

When a STL collection is destroyed, the corresponding destructor of the contained object is called.

This means that if you have

class YourObject {
  YourObject() { }
  ~YourObject() { }
}

map<int, YourObject> data;

Then the destructor of YourObject is called.

On the other hand, if you are storing pointers to object like in

map<int, YourObject*> data

Then the destruct of the pointer is called, which releases the pointer itself but without calling the pointed constructor.

The solution is to use something that can hold your object, like a shared_ptr, that is a special object that will care about calling the holded item object when there are no more references to it.

Example:

map<int, shared_ptr<YourObject>>
1
votes

If you ignore the type of container you're dealing with an just think of it as a container, you'll notice that anything you put in the container is owned by whomever owns the container. This also means that it's up to the owner to delete that memory. Your approach is sufficient to deallocate the memory that you allocated. Because the map object itself is a stack-allocated object, it's destructor will be called automatically.

Alternatively, a best practice for this type of situation is to use shared_ptr or unique_ptr, rather than a raw pointer. These wrapper classes will deallocate the memory for you, automatically.

map<int shared_ptr<int>> a;

See http://en.cppreference.com/w/cpp/memory

0
votes

The short answer is that the container will normally take care of deleting its contents when the container itself is destroyed.

It does that by destroying the objects in the container. As such, if you wanted to badly enough, you could create a type that mismanaged its memory by allocating memory (e.g., in its ctor) but doesn't release it properly. That should obviously be fixed by correcting the design of those objects though (e.g., adding a dtor that releases the memory they own). Alternatively, you could get the same effect by just storing a raw pointer.

Likewise, you could create an Allocator that didn't work correctly -- that allocated memory but did nothing when asked to release memory.

In every one of these cases, the real answer is "just don't do that".

0
votes

If you have to write a destructor (or cctor or op=) it indicates you likely do something wrong. If you do it to deallocate a resource more likely so.

The exception is the RAII handler for resources, that does nothing else.

In regular classes you use proper members and base classes, so your dtor has no work of its own.

STL classes all handle themselves, so having a map you have no obligations. Unless you filled it with dumb pointers to allocated memory or something like that -- where the first observation kicks in.

You second X::X() sample is broken in many ways, if exception is thrown on the 5th new you leak the first 4. And if you want to handle that situatuion by hand you end up with mess of a code.
That is all avoided if you use a proper smart thing, like unique_ptr or shared_ptr instead of int*.