• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1++++++++++++++++++++++++++++++++++
2 |Boost| Pointer Container Library
3++++++++++++++++++++++++++++++++++
4
5.. |Boost| image:: boost.png
6
7========
8Tutorial
9========
10
11The tutorial shows you the most simple usage of the
12library. It is assumed that the reader is familiar
13with the use of standard containers. Although
14the tutorial is devided into sections, it is recommended
15that you read it all from top to bottom.
16
17* `Basic usage`_
18* `Indirected interface`_
19* `Sequence containers`_
20* `Associative containers`_
21* `Null values`_
22* `Cloneability`_
23* `New functions`_
24* `Compatible smart pointer overloads`_
25* `Algorithms`_
26
27Basic usage
28-----------
29
30The most important aspect of a pointer container is that it manages
31memory for you. This means that you in most cases do not need to worry
32about deleting memory.
33
34Let us assume that we have an OO-hierarchy of animals
35
36.. parsed-literal::
37
38    class animal : `boost::noncopyable <http://www.boost.org/libs/utility/utility.htm#Class_noncopyable>`_
39    {
40    public:
41        virtual      ~animal()   {}
42        virtual void eat()       = 0;
43        virtual int  age() const = 0;
44        // ...
45    };
46
47    class mammal : public animal
48    {
49        // ...
50    };
51
52    class bird : public animal
53    {
54        // ...
55    };
56
57
58Then the managing of the animals is straight-forward. Imagine a
59Zoo::
60
61    class zoo
62    {
63        boost::ptr_vector<animal> the_animals;
64    public:
65
66        void add_animal( animal* a )
67        {
68            the_animals.push_back( a );
69        }
70    };
71
72Notice how we just pass the class name to the container; there
73is no ``*`` to indicate it is a pointer.
74With this declaration we can now say::
75
76    zoo the_zoo;
77    the_zoo.add_animal( new mammal("joe") );
78    the_zoo.add_animal( new bird("dodo") );
79
80Thus we heap-allocate all elements of the container
81and never rely on copy-semantics.
82
83Indirected interface
84--------------------
85
86A particular feature of the pointer containers is that
87the query interface is indirected. For example, ::
88
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
92
93This indirection also happens to iterators, so ::
94
95    typedef std::vector<animal*> std_vec;
96    std_vec vec;
97    ...
98    std_vec::iterator i = vec.begin();
99    (*i)->eat(); // '*' needed
100
101now becomes ::
102
103    typedef boost::ptr_vector<animal>  ptr_vec;
104    ptr_vec vec;
105    ptr_vec::iterator i = vec.begin();
106    i->eat(); // no indirection needed
107
108
109Sequence containers
110-------------------
111
112The sequence containers are used when you do not need to
113keep an ordering on your elements. You can basically
114expect all operations of the normal standard containers
115to be available. So, for example, with a  ``ptr_deque``
116and ``ptr_list`` object you can say::
117
118    boost::ptr_deque<animal> deq;
119    deq.push_front( new animal );
120    deq.pop_front();
121
122because ``std::deque`` and ``std::list`` have ``push_front()``
123and ``pop_front()`` members.
124
125If the standard sequence supports
126random access, so does the pointer container; for example::
127
128    for( boost::ptr_deque<animal>::size_type i = 0u;
129         i != deq.size(); ++i )
130         deq[i].eat();
131
132The ``ptr_vector`` also allows you to specify the size of
133the buffer to allocate; for example ::
134
135    boost::ptr_vector<animal> animals( 10u );
136
137will reserve room for 10 animals.
138
139Associative containers
140----------------------
141
142To keep an ordering on our animals, we could use a ``ptr_set``::
143
144    boost::ptr_set<animal> set;
145    set.insert( new monkey("bobo") );
146    set.insert( new whale("anna") );
147    ...
148
149This requires that ``operator<()`` is defined for animals. One
150way to do this could be ::
151
152    inline bool operator<( const animal& l, const animal& r )
153    {
154        return l.name() < r.name();
155    }
156
157if we wanted to keep the animals sorted by name.
158
159Maybe you want to keep all the animals in zoo ordered wrt.
160their name, but it so happens that many animals have the
161same name. We can then use a ``ptr_multimap``::
162
163    typedef boost::ptr_multimap<std::string,animal> zoo_type;
164    zoo_type zoo;
165    std::string bobo = "bobo",
166                anna = "anna";
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) );
171
172Note that must create the key as an lvalue
173(due to exception-safety issues); the following would not
174have compiled ::
175
176    zoo.insert( "bobo", // this is bad, but you get compile error
177                new monkey("bobo") );
178
179If a multimap is not needed, we can use ``operator[]()``
180to avoid the clumsiness::
181
182    boost::ptr_map<std::string,animal> animals;
183    animals["bobo"].set_name("bobo");
184
185This requires a default constructor for animals and
186a function to do the initialization, in this case ``set_name()``.
187
188A better alternative is to use `Boost.Assign <../../assign/index.html>`_
189to help you out. In particular, consider
190
191- `ptr_push_back(), ptr_push_front(), ptr_insert() and ptr_map_insert() <../../assign/doc/index.html#ptr_push_back>`_
192
193- `ptr_list_of() <../../assign/doc/index.html#ptr_list_of>`_
194
195For example, the above insertion may now be written ::
196
197     boost::ptr_multimap<std::string,animal> animals;
198
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" );
204
205
206Null values
207-----------
208
209By default, if you try to insert null into a container, an exception
210is thrown. If you want to allow nulls, then you must
211say so explicitly when declaring the container variable ::
212
213    boost::ptr_vector< boost::nullable<animal> > animals_type;
214    animals_type animals;
215    ...
216    animals.insert( animals.end(), new dodo("fido") );
217    animals.insert( animals.begin(), 0 ) // ok
218
219Once you have inserted a null into the container, you must
220always check if the value is null before accessing the object ::
221
222    for( animals_type::iterator i = animals.begin();
223         i != animals.end(); ++i )
224    {
225        if( !boost::is_null(i) ) // always check for validity
226            i->eat();
227    }
228
229If the container support random access, you may also check this as ::
230
231    for( animals_type::size_type i = 0u;
232         i != animals.size(); ++i )
233    {
234        if( !animals.is_null(i) )
235             animals[i].eat();
236    }
237
238Note that it is meaningless to insert
239null into ``ptr_set`` and ``ptr_multiset``.
240
241Cloneability
242------------
243
244In OO programming it is typical to prohibit copying of objects; the
245objects may sometimes be allowed to be Cloneable; for example,::
246
247    animal* animal::clone() const
248    {
249        return do_clone(); // implemented by private virtual function
250    }
251
252If the OO hierarchy thus allows cloning, we need to tell the
253pointer containers how cloning is to be done. This is simply
254done by defining a free-standing function, ``new_clone()``,
255in the same namespace as
256the object hierarchy::
257
258    inline animal* new_clone( const animal& a )
259    {
260        return a.clone();
261    }
262
263That is all, now a lot of functions in a pointer container
264can exploit the cloneability of the animal objects. For example ::
265
266    typedef boost::ptr_list<animal> zoo_type;
267    zoo_type zoo, another_zoo;
268    ...
269    another_zoo.assign( zoo.begin(), zoo.end() );
270
271will fill another zoo with clones of the first zoo. Similarly,
272``insert()`` can now insert clones into your pointer container ::
273
274    another_zoo.insert( another_zoo.begin(), zoo.begin(), zoo.end() );
275
276The whole container can now also be cloned ::
277
278    zoo_type yet_another_zoo = zoo.clone();
279
280Copying or assigning the container has the same effect as cloning (though it is slightly cheaper)::
281
282    zoo_type yet_another_zoo = zoo;
283
284Copying also support derived-to-base class conversions::
285
286    boost::ptr_vector<monkey> monkeys = boost::assign::ptr_list_of<monkey>( "bobo" )( "bebe")( "uhuh" );
287    boost::ptr_vector<animal> animals = monkeys;
288
289This also works for maps::
290
291    boost::ptr_map<std::string,monkey> monkeys = ...;
292    boost::ptr_map<std::string,animal> animals = monkeys;
293
294New functions
295-------------
296
297Given that we know we are working with pointers, a few new functions
298make sense. For example, say you want to remove an
299animal from the zoo ::
300
301    zoo_type::auto_type the_animal = zoo.release( zoo.begin() );
302    the_animal->eat();
303    animal* the_animal_ptr = the_animal.release(); // now this is not deleted
304    zoo.release(2); // for random access containers
305
306You can think of ``auto_type`` as a non-copyable form of
307``std::auto_ptr``. Notice that when you release an object, the
308pointer is removed from the container and the containers size
309shrinks. For containers that store nulls, we can exploit that
310``auto_type`` is convertible to ``bool``::
311
312    if( ptr_vector< nullable<T> >::auto_type r = vec.pop_back() )
313    {
314      ...
315    }
316
317You can also release the entire container if you
318want to return it from a function ::
319
320    compatible-smart-ptr< boost::ptr_deque<animal> > get_zoo()
321    {
322        boost::ptr_deque<animal>  result;
323        ...
324        return result.release(); // give up ownership
325    }
326    ...
327    boost::ptr_deque<animal> animals = get_zoo();
328
329Let us assume we want to move an animal object from
330one zoo to another. In other words, we want to move the
331animal and the responsibility of it to another zoo ::
332
333    another_zoo.transfer( another_zoo.end(), // insert before end
334                          zoo.begin(),       // insert this animal ...
335                          zoo );             // from this container
336
337This kind of "move-semantics" is different from
338normal value-based containers. You can think of ``transfer()``
339as the same as ``splice()`` on ``std::list``.
340
341If you want to replace an element, you can easily do so ::
342
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
345
346A map is slightly different to iterate over than standard maps.
347Now we say ::
348
349    typedef boost::ptr_map<std::string, boost::nullable<animal> > animal_map;
350    animal_map map;
351    ...
352    for( animal_map::const_iterator i = map.begin(), e = map.end(); i != e; ++i )
353    {
354        std::cout << "\n key: " << i->first;
355        std::cout << "\n age: ";
356
357        if( boost::is_null(i) )
358            std::cout << "unknown";
359        else
360            std::cout << i->second->age();
361     }
362
363Except for the check for null, this looks like it would with a normal map. But if ``age()`` had
364not been a ``const`` member function,
365it would not have compiled.
366
367Maps can also be indexed with bounds-checking ::
368
369    try
370    {
371        animal& bobo = map.at("bobo");
372    }
373    catch( boost::bad_ptr_container_operation& e )
374    {
375        // "bobo" not found
376    }
377
378Compatible smart pointer overloads
379----------------------------------
380
381Every time there is a function that takes a ``T*`` parameter, there is
382also a function overload (or two) taking a ``compatible-smart-ptr<U>``
383parameter. This is of course done to make the library intregrate
384seamlessly with ``std::auto_ptr`` or ``std::unique_ptr``. For example,
385consider a statement like ::
386
387  std::ptr_vector<Base> vec;
388  vec.push_back( new Base );
389
390If the compiler supports ``std::auto_ptr``, this is complemented
391by ::
392
393  std::auto_ptr<Derived> p( new Derived );
394  vec.push_back( p );
395
396Similarly if ``std::unique_ptr`` is available, we can write ::
397
398  std::unique_ptr<Derived> p( new Derived );
399  vec.push_back( std::move( p ) );
400
401Notice that the template argument for ``compatible-smart-ptr`` does not need to
402follow the template argument for ``ptr_vector`` as long as ``Derived*``
403can be implicitly converted to ``Base*``.
404
405Algorithms
406----------
407
408Unfortunately it is not possible to use pointer containers with
409mutating algorithms from the standard library. However,
410the most useful ones
411are instead provided as member functions::
412
413    boost::ptr_vector<animal> zoo;
414    ...
415    zoo.sort();                               // assume 'bool operator<( const animal&, const animal& )'
416    zoo.sort( std::less<animal>() );          // the same, notice no '*' is present
417    zoo.sort( zoo.begin(), zoo.begin() + 5 ); // sort selected range
418
419Notice that predicates are automatically wrapped in an `indirect_fun`_ object.
420
421..  _`indirect_fun`: indirect_fun.html
422
423You can remove equal and adjacent elements using ``unique()``::
424
425    zoo.unique();                             // assume 'bool operator==( const animal&, const animal& )'
426    zoo.unique( zoo.begin(), zoo.begin() + 5, my_comparison_predicate() );
427
428If you just want to remove certain elements, use ``erase_if``::
429
430    zoo.erase_if( my_predicate() );
431
432Finally you may want to merge two sorted containers::
433
434    boost::ptr_vector<animal> another_zoo = ...;
435    another_zoo.sort();                      // sorted wrt. to same order as 'zoo'
436    zoo.merge( another_zoo );
437    BOOST_ASSERT( another_zoo.empty() );
438
439That is all; now you have learned all the basics!
440
441.. raw:: html
442
443        <hr>
444
445**See also**
446
447- `Usage guidelines <guidelines.html>`_
448
449- `Cast utilities <../../conversion/cast.htm#Polymorphic_castl>`_
450
451**Navigate**
452
453- `home <ptr_container.html>`_
454- `examples <examples.html>`_
455
456.. raw:: html
457
458        <hr>
459
460:Copyright:     Thorsten Ottosen 2004-2006. Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see LICENSE_1_0.txt__).
461
462__ http://www.boost.org/LICENSE_1_0.txt
463
464