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