Showing posts with label custom deleters. Show all posts
Showing posts with label custom deleters. Show all posts

Handling dynamic array allocations

One of the recent questions on the forums, that I answered to recently, was related to using std::auto_ptr<> with allocations from array form of new. The need to know was - if the following would work?

        std::auto_ptr<int> ptr(new int[100]);

if not then why not?

The answer was simple if one knows what std::auto_ptr<> is good for, what its deleter is. std::auto_ptr<> uses delete operator to destruct owned pointer (pointing to a dynamic allocation made via new operator). So, it comes down to mixing of constructs like array form of new and simple delete. That is undefined behaviour as per the standards. You can read up more about these operators here - Free New, Delete Malloc

By design, the standard auto_ptr<> smart pointer is not suitable for dynamic array allocations. There can be many alternatives though. Few of those are as listed below:

  • Avoid self memory management and instead use std::vector<> / std::deque<> for dynamic arrays.
  • Use boost::scoped_array<>/boost::shared_array<>/boost::shared_ptr<> with a custom deleter (delete[]) passed in.
  • Write your own smart pointer : auto_ptr_array<> (same implementation as std::auto_ptr<>) just that it uses array form of delete instead of delete.

For the second point above - you can read more about custom deleters and shared_ptr<> here - Custom deleters with smart pointers



A boost::shared_array<>/boost::shared_ptr<> might not be the right choice if you don't want the reference count overhead of shared ownership which really should not be required when talking about auto_ptr<>s but if that's not an issue to worry about, it should work fine.

Another confusion arose, why doesn't std::auto_ptr<> do a delete[] instead of delete? Well, again, because it is not intended for array allocations. It would hence not work for singular objects created on the free store. This is because of the way delete[] might be implemented by the compiler.

There can be many ways in which delete[] could be implemented. Two of those are explained here - How does delete[] know how many elements to destroy? It is important for delete[] to know how many elements to destroy particularly since it is used with non-POD types as well and there, the destructor calls are necessary to be made for each of the to-be-destructed elements of the array.

The first method listed in the C++ FAQ Lite uses an extra allocation to remember the size you ask for at the runtime, if you used delete[] for single allocations, the compiler might try to interpret the block before the pointer you call it on as being size but since that was not allocated in the first place (new[] was not used!) - expect anything to happen, a crash or any other form of runtime error (depending upon how that error is translated on a particular system).

For the second, where array length association might not exist, it could cause anything depending upon a number of factors about the compiler implementation. One such possibility is, it could leave the variable uninitialized (yeah, its a bad thing, may not actually be happening, but who knows for sure about all compilers? We are just talking of possibilities here) - in which case the size could be anything - even 2 which goes beyond the buffer you allocated and again can cause any fault to the system/system free store. If the size is initialized to 0, it could leave the memory untouched and hence lost resulting in a leak. Memory leaks can be really dangerous particularly on systems that don't reclaim the leaked blocks by a process.

So, when the standard says, its undefined behaviour, it can result in anything. Worse could happen and thinking about these issues beyond the statement of "undefined behaviour" is rather silly. There can be many such "interesting" stories that one can write up. The key is, use std::auto_ptr<> what it is suited for, use delete/delete[] what they are suited for. Mixing them is really dangerous.

There is a peculiarity to note though. If you do the allocation for the single element using array form of new - you could use delete[] for that single element. That is:

        int * ptr = new int[1];
        delete[] ptr; //OK

Well, not really a peculiarity, but yeah, something to take note of.

Forum reference : What's wrong with std::auto_ptr<int> ptr(new int[100]);?

Custom deleters with smart pointers

There is just one smart pointer as part of the standard C++ library as of now (excluding the tr1 and proposals). That is the std::auto_ptr.

It is crucial to know that you can only use it for allocations made via the new operator. It neither handles, the allocations made via the array form of new or malloc or any other allocation routine provided, for that matter.

How can you extend it to be able to use it with those? Basically, you can't. There are an option though - you lay down your own version of auto_ptrs specific to those which have the delete call replaced by free/delete[]/or the relevant deleter. This will ask for writing down multiple classes for them and using them as appropriate. There is another way to extend it but before that let's look at how boost::shared_ptr handles it.

Boost shared_ptr has a concept for a custom deleter. The deleter would operate on the pointer (when the reference count drops to 0 - remembers the ownership is shared!) and the effect should be that of freeing the resource. The following is an illustration:

[CODE]

template<typename T>
struct mallocDeleter
{
    void operator() (T*& ptr)
    {
        if (ptr)
        {
            free(ptr);
            ptr=NULL;
        }
    }
};
void somefunction()
{
    MyType* ptr = getAllocatedPointer();//returns pointer to memory allocated by malloc
    shared_ptr<MyType, mallocDeleter<MyType> >object(ptr, mallocDeleter());
}

//scope ends shared_ptr's destructor gets called that then called mallocDeleter(ptr) - The default deleter is "delete".

Notice, how this expresses the flexibility. You could have acquired any other resource and would have wrapped it inside a shared_ptr and would provide the deleter to free that resource. For example, a File Handle - on which the custom deleter calls close (for C++ fstream objects, you don't need that - they already exploit RAII). Or, a db connection handle, for which the deleter would handle closing it. Just about anything that is manually allocated! Amazing how the ability to provide the deleter makes this smartness so generic.

I wonder why the auto_ptr or the boost scoped_ptr don't provide this ability. RAII is truely magic and takes care of resource leak issues so well.

Cheers!

Check out this stream