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?
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
ifx == 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.