c++boostmemory-leakssmart-pointersboost-ptr-container

How to safely fill Boosts's pointer containers?


The first example for Boost Pointer Container adds a raw pointer to the structure:

class zoo
{
    boost::ptr_vector<animal> the_animals;
public:

    void add_animal( animal* a )
    {
        the_animals.push_back( a );
    }
};

But what if push_back, or any other member function that might trigger reallocation, throws an exception during the process? From my understanding, in that case, it would be up to the caller to manage the memory for the given object, but because the caller passes a raw pointer to a class whose purpose is to manage memory, it's most likely that the caller doesn't.

So, wouldn't one need to use some kind of unique smart pointer in the code example above to wrap the pointer before providing it to the container and to be absolutely sure that no memory leaks? The containers do provide an overload for such smart pointers, but they don't enforce their usage.

Or is the argument that the containers simply won't ever throw exceptions during any add operation and it always succeeds?


Solution

  • The concerns you have are very valid if you were using std::vector<animal*> instead of boost::ptr_vector<animal>. Boost.PointerContainer is designed to handle pointers to resources that need to be freed, so it absolves you from having to worry about such things.

    The Boost documentation provides exception safety guarantees for the various member functions. ptr_vector inherits push_back from ptr_sequence_adaptor, and that lists the behavior of push_back as

    void push_back( T* x );
    

    Requirements: x != 0
    Effects: Inserts the pointer into container and takes ownership of it
    Throws: bad_pointer if x == 0
    Exception safety: Strong guarantee

    The strong guarantee means that if push_back throws, the state of the container is rolled back to what it was immediately prior to the call to push_back and no resource is leaked. Now, you could argue that that doesn't guarantee anything about the resource you were trying to add to the container, but it would be very bad form on the part of the implementation to allow that resource to leak, especially since the call to push_back is supposed to take ownership of the object being passed by the caller.

    If we look at the implementation of push_back, it shows how ptr_vector guarantees that the resource you're trying to add is not leaked.

    void push_back(value_type x)  // strong               
    {
        this->enforce_null_policy(x, "Null pointer in 'push_back()'");
    
        auto_type ptr(x);           // notrow
        this->base().push_back(x);  // strong, commit
        ptr.release();                // nothrow
    }
    

    So an auto_type is first constructed prior to the actual push_back operation being attempted. A bit more digging around reveals that auto_type is an alias for static_move_ptr, which is a smart pointer type that will, when necessary, free the resource it owns upon destruction.

    Thus, in the example shown, the animal * you're trying to add will never be leaked, even if an exception is thrown.