1 ++++++++++++++++++++++++++++++++++
2 |Boost| Pointer Container Library
3 ++++++++++++++++++++++++++++++++++
5 .. |Boost| image:: boost.png
11 The tutorial shows you the most simple usage of the
12 library. It is assumed that the reader is familiar
13 with the use of standard containers. Although
14 the tutorial is devided into sections, it is recommended
15 that you read it all from top to bottom.
18 * `Indirected interface`_
19 * `Sequence containers`_
20 * `Associative containers`_
24 * `std::auto_ptr<U> overloads`_
30 The most important aspect of a pointer container is that it manages
31 memory for you. This means that you in most cases do not need to worry
32 about deleting memory.
34 Let us assume that we have an OO-hierarchy of animals
38 class animal : `boost::noncopyable <http://www.boost.org/libs/utility/utility.htm#Class_noncopyable>`_
42 virtual void eat() = 0;
43 virtual int age() const = 0;
47 class mammal : public animal
52 class bird : public animal
58 Then the managing of the animals is straight-forward. Imagine a
63 boost::ptr_vector<animal> the_animals;
66 void add_animal( animal* a )
68 the_animals.push_back( a );
72 Notice how we just pass the class name to the container; there
73 is no ``*`` to indicate it is a pointer.
74 With this declaration we can now say::
77 the_zoo.add_animal( new mammal("joe") );
78 the_zoo.add_animal( new bird("dodo") );
80 Thus we heap-allocate all elements of the container
81 and never rely on copy-semantics.
86 A particular feature of the pointer containers is that
87 the query interface is indirected. For example, ::
89 boost::ptr_vector<animal> vec;
90 vec.push_back( new animal ); // you add it as pointer ...
91 vec[0].eat(); // but get a reference back
93 This indirection also happens to iterators, so ::
95 typedef std::vector<animal*> std_vec;
98 std_vec::iterator i = vec.begin();
99 (*i)->eat(); // '*' needed
103 typedef boost::ptr_vector<animal> ptr_vec;
105 ptr_vec::iterator i = vec.begin();
106 i->eat(); // no indirection needed
112 The sequence containers are used when you do not need to
113 keep an ordering on your elements. You can basically
114 expect all operations of the normal standard containers
115 to be available. So, for example, with a ``ptr_deque``
116 and ``ptr_list`` object you can say::
118 boost::ptr_deque<animal> deq;
119 deq.push_front( new animal );
122 because ``std::deque`` and ``std::list`` have ``push_front()``
123 and ``pop_front()`` members.
125 If the standard sequence supports
126 random access, so does the pointer container; for example::
128 for( boost::ptr_deque<animal>::size_type i = 0u;
129 i != deq.size(); ++i )
132 The ``ptr_vector`` also allows you to specify the size of
133 the buffer to allocate; for example ::
135 boost::ptr_vector<animal> animals( 10u );
137 will reserve room for 10 animals.
139 Associative containers
140 ----------------------
142 To keep an ordering on our animals, we could use a ``ptr_set``::
144 boost::ptr_set<animal> set;
145 set.insert( new monkey("bobo") );
146 set.insert( new whale("anna") );
149 This requires that ``operator<()`` is defined for animals. One
150 way to do this could be ::
152 inline bool operator<( const animal& l, const animal& r )
154 return l.name() < r.name();
157 if we wanted to keep the animals sorted by name.
159 Maybe you want to keep all the animals in zoo ordered wrt.
160 their name, but it so happens that many animals have the
161 same name. We can then use a ``ptr_multimap``::
163 typedef boost::ptr_multimap<std::string,animal> zoo_type;
165 std::string bobo = "bobo",
167 zoo.insert( bobo, new monkey(bobo) );
168 zoo.insert( bobo, new elephant(bobo) );
169 zoo.insert( anna, new whale(anna) );
170 zoo.insert( anna, new emu(anna) );
172 Note that must create the key as an lvalue
173 (due to exception-safety issues); the following would not
176 zoo.insert( "bobo", // this is bad, but you get compile error
177 new monkey("bobo") );
179 If a multimap is not needed, we can use ``operator[]()``
180 to avoid the clumsiness::
182 boost::ptr_map<std::string,animal> animals;
183 animals["bobo"].set_name("bobo");
185 This requires a default constructor for animals and
186 a function to do the initialization, in this case ``set_name()``.
188 A better alternative is to use `Boost.Assign <../../assign/index.html>`_
189 to help you out. In particular, consider
191 - `ptr_push_back(), ptr_push_front(), ptr_insert() and ptr_map_insert() <../../assign/doc/index.html#ptr_push_back>`_
193 - `ptr_list_of() <../../assign/doc/index.html#ptr_list_of>`_
195 For example, the above insertion may now be written ::
197 boost::ptr_multimap<std::string,animal> animals;
199 using namespace boost::assign;
200 ptr_map_insert<monkey>( animals )( "bobo", "bobo" );
201 ptr_map_insert<elephant>( animals )( "bobo", "bobo" );
202 ptr_map_insert<whale>( animals )( "anna", "anna" );
203 ptr_map_insert<emu>( animals )( "anna", "anna" );
209 By default, if you try to insert null into a container, an exception
210 is thrown. If you want to allow nulls, then you must
211 say so explicitly when declaring the container variable ::
213 boost::ptr_vector< boost::nullable<animal> > animals_type;
214 animals_type animals;
216 animals.insert( animals.end(), new dodo("fido") );
217 animals.insert( animals.begin(), 0 ) // ok
219 Once you have inserted a null into the container, you must
220 always check if the value is null before accessing the object ::
222 for( animals_type::iterator i = animals.begin();
223 i != animals.end(); ++i )
225 if( !boost::is_null(i) ) // always check for validity
229 If the container support random access, you may also check this as ::
231 for( animals_type::size_type i = 0u;
232 i != animals.size(); ++i )
234 if( !animals.is_null(i) )
238 Note that it is meaningless to insert
239 null into ``ptr_set`` and ``ptr_multiset``.
244 In OO programming it is typical to prohibit copying of objects; the
245 objects may sometimes be allowed to be Cloneable; for example,::
247 animal* animal::clone() const
249 return do_clone(); // implemented by private virtual function
252 If the OO hierarchy thus allows cloning, we need to tell the
253 pointer containers how cloning is to be done. This is simply
254 done by defining a free-standing function, ``new_clone()``,
255 in the same namespace as
256 the object hierarchy::
258 inline animal* new_clone( const animal& a )
263 That is all, now a lot of functions in a pointer container
264 can exploit the cloneability of the animal objects. For example ::
266 typedef boost::ptr_list<animal> zoo_type;
267 zoo_type zoo, another_zoo;
269 another_zoo.assign( zoo.begin(), zoo.end() );
271 will fill another zoo with clones of the first zoo. Similarly,
272 ``insert()`` can now insert clones into your pointer container ::
274 another_zoo.insert( another_zoo.begin(), zoo.begin(), zoo.end() );
276 The whole container can now also be cloned ::
278 zoo_type yet_another_zoo = zoo.clone();
280 Copying or assigning the container has the same effect as cloning (though it is slightly cheaper)::
282 zoo_type yet_another_zoo = zoo;
284 Copying also support derived-to-base class conversions::
286 boost::ptr_vector<monkey> monkeys = boost::assign::ptr_list_of<monkey>( "bobo" )( "bebe")( "uhuh" );
287 boost::ptr_vector<animal> animals = monkeys;
289 This also works for maps::
291 boost::ptr_map<std::string,monkey> monkeys = ...;
292 boost::ptr_map<std::string,animal> animals = monkeys;
297 Given that we know we are working with pointers, a few new functions
298 make sense. For example, say you want to remove an
299 animal from the zoo ::
301 zoo_type::auto_type the_animal = zoo.release( zoo.begin() );
303 animal* the_animal_ptr = the_animal.release(); // now this is not deleted
304 zoo.release(2); // for random access containers
306 You can think of ``auto_type`` as a non-copyable form of
307 ``std::auto_ptr``. Notice that when you release an object, the
308 pointer is removed from the container and the containers size
309 shrinks. For containers that store nulls, we can exploit that
310 ``auto_type`` is convertible to ``bool``::
312 if( ptr_vector< nullable<T> >::auto_type r = vec.pop_back() )
317 You can also release the entire container if you
318 want to return it from a function ::
320 std::auto_ptr< boost::ptr_deque<animal> > get_zoo()
322 boost::ptr_deque<animal> result;
324 return result.release(); // give up ownership
327 boost::ptr_deque<animal> animals = get_zoo();
329 Let us assume we want to move an animal object from
330 one zoo to another. In other words, we want to move the
331 animal and the responsibility of it to another zoo ::
333 another_zoo.transfer( another_zoo.end(), // insert before end
334 zoo.begin(), // insert this animal ...
335 zoo ); // from this container
337 This kind of "move-semantics" is different from
338 normal value-based containers. You can think of ``transfer()``
339 as the same as ``splice()`` on ``std::list``.
341 If you want to replace an element, you can easily do so ::
343 zoo_type::auto_type old_animal = zoo.replace( zoo.begin(), new monkey("bibi") );
344 zoo.replace( 2, old_animal.release() ); // for random access containers
346 A map is slightly different to iterate over than standard maps.
349 typedef boost::ptr_map<std::string, boost::nullable<animal> > animal_map;
352 for( animal_map::const_iterator i = map.begin(), e = map.end(); i != e; ++i )
354 std::cout << "\n key: " << i->first;
355 std::cout << "\n age: ";
357 if( boost::is_null(i) )
358 std::cout << "unknown";
360 std::cout << i->second->age();
363 Except for the check for null, this looks like it would with a normal map. But if ``age()`` had
364 not been a ``const`` member function,
365 it would not have compiled.
367 Maps can also be indexed with bounds-checking ::
371 animal& bobo = map.at("bobo");
373 catch( boost::bad_ptr_container_operation& e )
378 ``std::auto_ptr<U>`` overloads
379 ------------------------------
381 Every time there is a function that takes a ``T*`` parameter, there is
382 also a function taking an ``std::auto_ptr<U>`` parameter. This is of course done
383 to make the library intregrate seamlessly with ``std::auto_ptr``. For example ::
385 std::ptr_vector<Base> vec;
386 vec.push_back( new Base );
388 is complemented by ::
390 std::auto_ptr<Derived> p( new Derived );
393 Notice that the template argument for ``std::auto_ptr`` does not need to
394 follow the template argument for ``ptr_vector`` as long as ``Derived*``
395 can be implicitly converted to ``Base*``.
400 Unfortunately it is not possible to use pointer containers with
401 mutating algorithms from the standard library. However,
403 are instead provided as member functions::
405 boost::ptr_vector<animal> zoo;
407 zoo.sort(); // assume 'bool operator<( const animal&, const animal& )'
408 zoo.sort( std::less<animal>() ); // the same, notice no '*' is present
409 zoo.sort( zoo.begin(), zoo.begin() + 5 ); // sort selected range
411 Notice that predicates are automatically wrapped in an `indirect_fun`_ object.
413 .. _`indirect_fun`: indirect_fun.html
415 You can remove equal and adjacent elements using ``unique()``::
417 zoo.unique(); // assume 'bool operator==( const animal&, const animal& )'
418 zoo.unique( zoo.begin(), zoo.begin() + 5, my_comparison_predicate() );
420 If you just want to remove certain elements, use ``erase_if``::
422 zoo.erase_if( my_predicate() );
424 Finally you may want to merge two sorted containers::
426 boost::ptr_vector<animal> another_zoo = ...;
427 another_zoo.sort(); // sorted wrt. to same order as 'zoo'
428 zoo.merge( another_zoo );
429 BOOST_ASSERT( another_zoo.empty() );
431 That is all; now you have learned all the basics!
439 - `Usage guidelines <guidelines.html>`_
441 - `Cast utilities <../../conversion/cast.htm#Polymorphic_castl>`_
445 - `home <ptr_container.html>`_
446 - `examples <examples.html>`_
452 :Copyright: Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see LICENSE_1_0.txt__).
454 __ http://www.boost.org/LICENSE_1_0.txt