]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | ++++++++++++++++++++++++++++++++++ |
2 | |Boost| Pointer Container Library | |
3 | ++++++++++++++++++++++++++++++++++ | |
4 | ||
5 | .. |Boost| image:: boost.png | |
6 | ||
7 | ======== | |
8 | Tutorial | |
9 | ======== | |
10 | ||
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. | |
16 | ||
17 | * `Basic usage`_ | |
18 | * `Indirected interface`_ | |
19 | * `Sequence containers`_ | |
20 | * `Associative containers`_ | |
21 | * `Null values`_ | |
22 | * `Cloneability`_ | |
23 | * `New functions`_ | |
24 | * `std::auto_ptr<U> overloads`_ | |
25 | * `Algorithms`_ | |
26 | ||
27 | Basic usage | |
28 | ----------- | |
29 | ||
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. | |
33 | ||
34 | Let 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 | ||
58 | Then the managing of the animals is straight-forward. Imagine a | |
59 | Zoo:: | |
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 | ||
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:: | |
75 | ||
76 | zoo the_zoo; | |
77 | the_zoo.add_animal( new mammal("joe") ); | |
78 | the_zoo.add_animal( new bird("dodo") ); | |
79 | ||
80 | Thus we heap-allocate all elements of the container | |
81 | and never rely on copy-semantics. | |
82 | ||
83 | Indirected interface | |
84 | -------------------- | |
85 | ||
86 | A particular feature of the pointer containers is that | |
87 | the 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 | ||
93 | This 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 | ||
101 | now 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 | ||
109 | Sequence containers | |
110 | ------------------- | |
111 | ||
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:: | |
117 | ||
118 | boost::ptr_deque<animal> deq; | |
119 | deq.push_front( new animal ); | |
120 | deq.pop_front(); | |
121 | ||
122 | because ``std::deque`` and ``std::list`` have ``push_front()`` | |
123 | and ``pop_front()`` members. | |
124 | ||
125 | If the standard sequence supports | |
126 | random 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 | ||
132 | The ``ptr_vector`` also allows you to specify the size of | |
133 | the buffer to allocate; for example :: | |
134 | ||
135 | boost::ptr_vector<animal> animals( 10u ); | |
136 | ||
137 | will reserve room for 10 animals. | |
138 | ||
139 | Associative containers | |
140 | ---------------------- | |
141 | ||
142 | To 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 | ||
149 | This requires that ``operator<()`` is defined for animals. One | |
150 | way 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 | ||
157 | if we wanted to keep the animals sorted by name. | |
158 | ||
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``:: | |
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 | ||
172 | Note that must create the key as an lvalue | |
173 | (due to exception-safety issues); the following would not | |
174 | have compiled :: | |
175 | ||
176 | zoo.insert( "bobo", // this is bad, but you get compile error | |
177 | new monkey("bobo") ); | |
178 | ||
179 | If a multimap is not needed, we can use ``operator[]()`` | |
180 | to avoid the clumsiness:: | |
181 | ||
182 | boost::ptr_map<std::string,animal> animals; | |
183 | animals["bobo"].set_name("bobo"); | |
184 | ||
185 | This requires a default constructor for animals and | |
186 | a function to do the initialization, in this case ``set_name()``. | |
187 | ||
188 | A better alternative is to use `Boost.Assign <../../assign/index.html>`_ | |
189 | to 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 | ||
195 | For 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 | ||
206 | Null values | |
207 | ----------- | |
208 | ||
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 :: | |
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 | ||
219 | Once you have inserted a null into the container, you must | |
220 | always 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 | ||
229 | If 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 | ||
238 | Note that it is meaningless to insert | |
239 | null into ``ptr_set`` and ``ptr_multiset``. | |
240 | ||
241 | Cloneability | |
242 | ------------ | |
243 | ||
244 | In OO programming it is typical to prohibit copying of objects; the | |
245 | objects 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 | ||
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:: | |
257 | ||
258 | inline animal* new_clone( const animal& a ) | |
259 | { | |
260 | return a.clone(); | |
261 | } | |
262 | ||
263 | That is all, now a lot of functions in a pointer container | |
264 | can 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 | ||
271 | will 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 | ||
276 | The whole container can now also be cloned :: | |
277 | ||
278 | zoo_type yet_another_zoo = zoo.clone(); | |
279 | ||
280 | Copying or assigning the container has the same effect as cloning (though it is slightly cheaper):: | |
281 | ||
282 | zoo_type yet_another_zoo = zoo; | |
283 | ||
284 | Copying 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 | ||
289 | This also works for maps:: | |
290 | ||
291 | boost::ptr_map<std::string,monkey> monkeys = ...; | |
292 | boost::ptr_map<std::string,animal> animals = monkeys; | |
293 | ||
294 | New functions | |
295 | ------------- | |
296 | ||
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 :: | |
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 | ||
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``:: | |
311 | ||
312 | if( ptr_vector< nullable<T> >::auto_type r = vec.pop_back() ) | |
313 | { | |
314 | ... | |
315 | } | |
316 | ||
317 | You can also release the entire container if you | |
318 | want to return it from a function :: | |
319 | ||
320 | std::auto_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 | ||
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 :: | |
332 | ||
333 | another_zoo.transfer( another_zoo.end(), // insert before end | |
334 | zoo.begin(), // insert this animal ... | |
335 | zoo ); // from this container | |
336 | ||
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``. | |
340 | ||
341 | If 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 | ||
346 | A map is slightly different to iterate over than standard maps. | |
347 | Now 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 | ||
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. | |
366 | ||
367 | Maps 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 | ||
378 | ``std::auto_ptr<U>`` overloads | |
379 | ------------------------------ | |
380 | ||
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 :: | |
384 | ||
385 | std::ptr_vector<Base> vec; | |
386 | vec.push_back( new Base ); | |
387 | ||
388 | is complemented by :: | |
389 | ||
390 | std::auto_ptr<Derived> p( new Derived ); | |
391 | vec.push_back( p ); | |
392 | ||
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*``. | |
396 | ||
397 | Algorithms | |
398 | ---------- | |
399 | ||
400 | Unfortunately it is not possible to use pointer containers with | |
401 | mutating algorithms from the standard library. However, | |
402 | the most useful ones | |
403 | are instead provided as member functions:: | |
404 | ||
405 | boost::ptr_vector<animal> zoo; | |
406 | ... | |
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 | |
410 | ||
411 | Notice that predicates are automatically wrapped in an `indirect_fun`_ object. | |
412 | ||
413 | .. _`indirect_fun`: indirect_fun.html | |
414 | ||
415 | You can remove equal and adjacent elements using ``unique()``:: | |
416 | ||
417 | zoo.unique(); // assume 'bool operator==( const animal&, const animal& )' | |
418 | zoo.unique( zoo.begin(), zoo.begin() + 5, my_comparison_predicate() ); | |
419 | ||
420 | If you just want to remove certain elements, use ``erase_if``:: | |
421 | ||
422 | zoo.erase_if( my_predicate() ); | |
423 | ||
424 | Finally you may want to merge two sorted containers:: | |
425 | ||
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() ); | |
430 | ||
431 | That is all; now you have learned all the basics! | |
432 | ||
433 | .. raw:: html | |
434 | ||
435 | <hr> | |
436 | ||
437 | **See also** | |
438 | ||
439 | - `Usage guidelines <guidelines.html>`_ | |
440 | ||
441 | - `Cast utilities <../../conversion/cast.htm#Polymorphic_castl>`_ | |
442 | ||
443 | **Navigate** | |
444 | ||
445 | - `home <ptr_container.html>`_ | |
446 | - `examples <examples.html>`_ | |
447 | ||
448 | .. raw:: html | |
449 | ||
450 | <hr> | |
451 | ||
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__). | |
453 | ||
454 | __ http://www.boost.org/LICENSE_1_0.txt | |
455 |