Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

This is the documentation for an old version of Boost. Click here to view this page for the latest version.

Boost Pointer Container Library

Examples

Some examples are given here and in the accompanying test files:

1. Null pointers cannot be stored in the containers

my_container.push_back( 0 );            // throws bad_ptr 
my_container.replace( an_iterator, 0 ); // throws bad_ptr
my_container.insert( an_iterator, 0 );  // throws bad_ptr       
std::auto_ptr<T> p( 0 );
my_container.push_back( p );            // throws bad_ptr                                                          

2. Iterators and other operations return indirected values

ptr_vector<X> pvec; 
std::vector<X*> vec;
*vec.begin()  = new X;   // fine, memory leak
*pvec.begin() = new X;   // compile time error
( *vec.begin() )->foo(); // call X::foo(), a bit clumsy
pvec.begin()->foo();     // no indirection needed
*vec.front()  = X();     // overwrite first element
pvec.front()  = X();     // no indirection needed

3. Copy-semantics of pointer containers

ptr_vector<T> vec1; 
...
ptr_vector<T> vec2( vec1.clone() ); // deep copy objects of 'vec1' and use them to construct 'vec2', could be very expensive
vec2 = vec1.release();              // give up ownership of pointers in 'vec1' and pass the ownership to 'vec2', rather cheap
vec2.release();                     // give up ownership; the objects will be deallocated if not assigned to another container
vec1 = vec2;                        // deep copy objects of 'vec2' and assign them to 'vec1', could be very expensive 
ptr_vector<T> vec3( vec1 );         // deep copy objects of 'vec1', could be very expensive

4. Making a non-copyable type Cloneable

 // a class that has no normal copy semantics
class X : boost::noncopyable { public: X* clone() const; ... };
                                                                   
// this will be found by the library by argument dependent lookup (ADL)                                                                  
X* new_clone( const X& x ) 
{ return x.clone(); }
                                                                   
// we can now use the interface that requires cloneability
ptr_vector<X> vec1, vec2;
...
vec2 = vec1.clone();                                 // 'clone()' requires cloning <g> 
vec2.insert( vec2.end(), vec1.begin(), vec1.end() ); // inserting always means inserting clones 

5. Objects are cloned before insertion, inserted pointers are owned by the container

class X { ... };                     // assume 'X' is Cloneable 
X x;                                 // and 'X' can be stack-allocated 
ptr_list<X> list; 
list.push_back( new_clone( x ) );    // insert a clone
list.push_back( new X );             // always give the pointer directly to the container to avoid leaks
list.push_back( &x );                // don't do this!!! 
std::auto_ptr<X> p( new X );
list.push_back( p );                 // give up ownership
BOOST_ASSERT( p.get() == 0 );

6. Transferring ownership of a single element

ptr_deque<T>                    deq; 
typedef ptr_deque<T>::auto_type auto_type;

// ... fill the container somehow

auto_type ptr  = deq.pop_back();                 // remove back element from container and give up ownership
auto_type ptr2 = deq.release( deq.begin() + 2 ); // use an iterator to determine the element to release
ptr            = deq.pop_front();                // supported for 'ptr_list' and 'ptr_deque'
                                
deq.push_back( ptr.release() );                  // give ownership back to the container

7. Transferring ownership of pointers between different pointer containers

ptr_list<X> list; ptr_vector<X> vec;
...
//
// note: no cloning happens in these examples                                
//
list.transfer( list.begin(), vec.begin(), vec );           // make the first element of 'vec' the first element of 'list'
vec.transfer( vec.end(), list.begin(), list.end(), list ); // put all the lists element into the vector                                 

We can also transfer objects from ptr_container<Derived> to ptr_container<Base> without any problems.

8. Selected test files

incomplete_type_test.cpp:
 Shows how to implement the Composite pattern.
simple_test.cpp:
 Shows how the usage of pointer container compares with a container of smart pointers
view_example.cpp:
 Shows how to use a pointer container as a view into other container
tree_test.cpp:Shows how to make a tree-structure
array_test.cpp:Shows how to make an n-ary tree

9. A large example

This example shows many of the most common features at work. The example provide lots of comments. The source code can also be found here.

//
// Boost.Pointer Container
//
//  Copyright Thorsten Ottosen 2003-2005. Use, modification and
//  distribution is subject to the Boost Software License, Version
//  1.0. (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//
// For more information, see http://www.boost.org/libs/ptr_container/
//

//
// This example is intended to get you started.
// Notice how the smart container
//
// 1. takes ownership of objects
// 2. transfers ownership
// 3. applies indirection to iterators 
// 4. clones objects from other smart containers
// 

//
// First we select which container to use.
//
#include <boost/ptr_container/ptr_deque.hpp>

//
// we need these later in the example
//
#include <boost/assert.hpp>
#include <string>
#include <exception>


//
// Then we define a small polymorphic class
// hierarchy.
// 

class animal : boost::noncopyable
{
    virtual std::string do_speak() const = 0;
    std::string name_;

protected:
    //
    // Animals cannot be copied...
    //
    animal( const animal& r ) : name_( r.name_ )           { }
    void operator=( const animal& );

private:
    //
    // ...but due to advances in genetics, we can clone them!
    //

    virtual animal* do_clone() const = 0;
        
public:
    animal( const std::string& name ) : name_(name)        { }
    virtual ~animal() throw()                              { }
    
    std::string speak() const
    {
        return do_speak();
    }

    std::string name() const
    {
        return name_;
    }

    animal* clone() const
    {
        return do_clone();
    }
};

//
// An animal is still not Clonable. We need this last hook.
//
// Notice that we pass the animal by const reference
// and return by pointer.
//

animal* new_clone( const animal& a )
{
    return a.clone();
}

//
// We do not need to define 'delete_clone()' since
// since the default is to call the default 'operator delete()'.
//

const std::string muuuh = "Muuuh!";
const std::string oiink = "Oiiink";

class cow : public animal
{
    virtual std::string do_speak() const
    {
        return muuuh;
    }

    virtual animal* do_clone() const
    {
        return new cow( *this );
    }

public:
    cow( const std::string& name ) : animal(name)          { }
};

class pig : public animal
{
    virtual std::string do_speak() const
    {
        return oiink;
    }

    virtual animal* do_clone() const
    {
        return new pig( *this );
    }
    
public:
    pig( const std::string& name ) : animal(name)          { }
};

//
// Then we, of course, need a place to put all
// those animals.
//

class farm
{
    //
    // This is where the smart containers are handy
    //
    typedef boost::ptr_deque<animal> barn_type;
    barn_type                        barn;

    //
    // An error type
    //
    struct farm_trouble : public std::exception           { };

public:
    // 
    // We would like to make it possible to
    // iterate over the animals in the farm
    //
    typedef barn_type::iterator  animal_iterator;

    //
    // We also need to count the farm's size...
    //
    typedef barn_type::size_type size_type;
    
    //
    // And we also want to transfer an animal
    // safely around. The easiest way to think
    // about '::auto_type' is to imagine a simplified
    // 'std::auto_ptr<T>' ... this means you can expect
    // 
    //   T* operator->()
    //   T* release()
    //   deleting destructor
    //
    // but not more.
    //
    typedef barn_type::auto_type  animal_transport;

    // 
    // Create an empty farm.
    //
    farm()                                                 { }
    
    //
    // We need a constructor that can make a new
    // farm by cloning a range of animals.
    //
    farm( animal_iterator begin, animal_iterator end )
     : 
        //
        // Objects are always cloned before insertion
        // unless we explicitly add a pointer or 
        // use 'release()'. Therefore we actually
        // clone all animals in the range
        //
        barn( begin, end )                               { }
    
    //
    // ... so we need some other function too
    //

    animal_iterator begin()
    {
        return barn.begin();
    }

    animal_iterator end()
    {
        return barn.end();
    }
    
    //
    // Here it is quite ok to have an 'animal*' argument.
    // The smart container will handle all ownership
    // issues.
    //
    void buy_animal( animal* a )
    {
        barn.push_back( a );
    }

    //
    // The farm can also be in economical trouble and
    // therefore be in the need to sell animals.
    //
    animal_transport sell_animal( animal_iterator to_sell )
    {
        if( to_sell == end() )
            throw farm_trouble();

        //
        // Here we remove the animal from the barn,
        // but the animal is not deleted yet...it's
        // up to the buyer to decide what
        // to do with it.
        //
        return barn.release( to_sell );
    }

    //
    // How big a farm do we have?
    //
    size_type size() const
    {
        return barn.size();
    }

    //
    // If things are bad, we might choose to sell all animals :-(
    //
    std::auto_ptr<barn_type> sell_farm()
    {
        return barn.release();
    }

    //
    // However, if things are good, we might buy somebody
    // else's farm :-)
    //

    void buy_farm( std::auto_ptr<barn_type> other )
    {
        //
        // This line inserts all the animals from 'other'
        // and is guaranteed either to succeed or to have no
        // effect
        //
        barn.transfer( barn.end(), // insert new animals at the end
                         *other );     // we want to transfer all animals,
                                       // so we use the whole container as argument
        //
        // You might think you would have to do
        //
        // other.release();
        //
        // but '*other' is empty and can go out of scope as it wants
        //
        BOOST_ASSERT( other->empty() );
    }
    
}; // class 'farm'.

int main()
{
    //
    // First we make a farm
    //
    farm animal_farm;
    BOOST_ASSERT( animal_farm.size() == 0u );
    
    animal_farm.buy_animal( new pig("Betty") );
    animal_farm.buy_animal( new pig("Benny") );
    animal_farm.buy_animal( new pig("Jeltzin") );
    animal_farm.buy_animal( new cow("Hanz") );
    animal_farm.buy_animal( new cow("Mary") );
    animal_farm.buy_animal( new cow("Frederik") );
    BOOST_ASSERT( animal_farm.size() == 6u );

    //
    // Then we make another farm...it will actually contain
    // a clone of the other farm.
    //
    farm new_farm( animal_farm.begin(), animal_farm.end() );
    BOOST_ASSERT( new_farm.size() == 6u );

    //
    // Is it really clones in the new farm?
    //
    BOOST_ASSERT( new_farm.begin()->name() == "Betty" );
    
    //
    // Then we search for an animal, Mary (the Crown Princess of Denmark),
    // because we would like to buy her ...
    //
    typedef farm::animal_iterator iterator;
    iterator to_sell;
    for( iterator i   = animal_farm.begin(),
                  end = animal_farm.end();
         i != end; ++i )
    {
        if( i->name() == "Mary" )
        {
            to_sell = i;
            break;
        }
    }

    farm::animal_transport mary = animal_farm.sell_animal( to_sell );


    if( mary->speak() == muuuh )
        //
        // Great, Mary is a cow, and she may live longer
        //
        new_farm.buy_animal( mary.release() );
    else
        //
        // Then the animal would be destroyed (!)
        // when we go out of scope.
        //
        ;

    //
    // Now we can observe some changes to the two farms...
    //
    BOOST_ASSERT( animal_farm.size() == 5u );
    BOOST_ASSERT( new_farm.size()    == 7u );

    //
    // The new farm has however underestimated how much
    // it cost to feed Mary and its owner is forced to sell the farm...
    //
    animal_farm.buy_farm( new_farm.sell_farm() );

    BOOST_ASSERT( new_farm.size()    == 0u );
    BOOST_ASSERT( animal_farm.size() == 12u );     
}

Navigate:


Copyright:Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see LICENSE_1_0.txt).