sl@0: // sl@0: // Boost.Pointer Container sl@0: // sl@0: // Copyright Thorsten Ottosen 2003-2005. Use, modification and sl@0: // distribution is subject to the Boost Software License, Version sl@0: // 1.0. (See accompanying file LICENSE_1_0.txt or copy at sl@0: // http://www.boost.org/LICENSE_1_0.txt) sl@0: // sl@0: // For more information, see http://www.boost.org/libs/ptr_container/ sl@0: // sl@0: sl@0: // sl@0: // This example is intended to get you started. sl@0: // Notice how the smart container sl@0: // sl@0: // 1. takes ownership of objects sl@0: // 2. transfers ownership sl@0: // 3. applies indirection to iterators sl@0: // 4. clones objects from other smart containers sl@0: // sl@0: sl@0: // sl@0: // First we select which container to use. sl@0: // sl@0: #include sl@0: sl@0: // sl@0: // we need these later in the example sl@0: // sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: sl@0: // sl@0: // Then we define a small polymorphic class sl@0: // hierarchy. sl@0: // sl@0: sl@0: class animal : boost::noncopyable sl@0: { sl@0: virtual std::string do_speak() const = 0; sl@0: std::string name_; sl@0: sl@0: protected: sl@0: // sl@0: // Animals cannot be copied... sl@0: // sl@0: animal( const animal& r ) : name_( r.name_ ) { } sl@0: void operator=( const animal& ); sl@0: sl@0: private: sl@0: // sl@0: // ...but due to advances in genetics, we can clone them! sl@0: // sl@0: sl@0: virtual animal* do_clone() const = 0; sl@0: sl@0: public: sl@0: animal( const std::string& name ) : name_(name) { } sl@0: virtual ~animal() throw() { } sl@0: sl@0: std::string speak() const sl@0: { sl@0: return do_speak(); sl@0: } sl@0: sl@0: std::string name() const sl@0: { sl@0: return name_; sl@0: } sl@0: sl@0: animal* clone() const sl@0: { sl@0: return do_clone(); sl@0: } sl@0: }; sl@0: sl@0: // sl@0: // An animal is still not Clonable. We need this last hook. sl@0: // sl@0: // Notice that we pass the animal by const reference sl@0: // and return by pointer. sl@0: // sl@0: sl@0: animal* new_clone( const animal& a ) sl@0: { sl@0: return a.clone(); sl@0: } sl@0: sl@0: // sl@0: // We do not need to define 'delete_clone()' since sl@0: // since the default is to call the default 'operator delete()'. sl@0: // sl@0: sl@0: const std::string muuuh = "Muuuh!"; sl@0: const std::string oiink = "Oiiink"; sl@0: sl@0: class cow : public animal sl@0: { sl@0: virtual std::string do_speak() const sl@0: { sl@0: return muuuh; sl@0: } sl@0: sl@0: virtual animal* do_clone() const sl@0: { sl@0: return new cow( *this ); sl@0: } sl@0: sl@0: public: sl@0: cow( const std::string& name ) : animal(name) { } sl@0: }; sl@0: sl@0: class pig : public animal sl@0: { sl@0: virtual std::string do_speak() const sl@0: { sl@0: return oiink; sl@0: } sl@0: sl@0: virtual animal* do_clone() const sl@0: { sl@0: return new pig( *this ); sl@0: } sl@0: sl@0: public: sl@0: pig( const std::string& name ) : animal(name) { } sl@0: }; sl@0: sl@0: // sl@0: // Then we, of course, need a place to put all sl@0: // those animals. sl@0: // sl@0: sl@0: class farm sl@0: { sl@0: // sl@0: // This is where the smart containers are handy sl@0: // sl@0: typedef boost::ptr_deque barn_type; sl@0: barn_type barn; sl@0: sl@0: // sl@0: // An error type sl@0: // sl@0: struct farm_trouble : public std::exception { }; sl@0: sl@0: public: sl@0: // sl@0: // We would like to make it possible to sl@0: // iterate over the animals in the farm sl@0: // sl@0: typedef barn_type::iterator animal_iterator; sl@0: sl@0: // sl@0: // We also need to count the farm's size... sl@0: // sl@0: typedef barn_type::size_type size_type; sl@0: sl@0: // sl@0: // And we also want to transfer an animal sl@0: // safely around. The easiest way to think sl@0: // about '::auto_type' is to imagine a simplified sl@0: // 'std::auto_ptr' ... this means you can expect sl@0: // sl@0: // T* operator->() sl@0: // T* release() sl@0: // deleting destructor sl@0: // sl@0: // but not more. sl@0: // sl@0: typedef barn_type::auto_type animal_transport; sl@0: sl@0: // sl@0: // Create an empty farm. sl@0: // sl@0: farm() { } sl@0: sl@0: // sl@0: // We need a constructor that can make a new sl@0: // farm by cloning a range of animals. sl@0: // sl@0: farm( animal_iterator begin, animal_iterator end ) sl@0: : sl@0: // sl@0: // Objects are always cloned before insertion sl@0: // unless we explicitly add a pointer or sl@0: // use 'release()'. Therefore we actually sl@0: // clone all animals in the range sl@0: // sl@0: barn( begin, end ) { } sl@0: sl@0: // sl@0: // ... so we need some other function too sl@0: // sl@0: sl@0: animal_iterator begin() sl@0: { sl@0: return barn.begin(); sl@0: } sl@0: sl@0: animal_iterator end() sl@0: { sl@0: return barn.end(); sl@0: } sl@0: sl@0: // sl@0: // Here it is quite ok to have an 'animal*' argument. sl@0: // The smart container will handle all ownership sl@0: // issues. sl@0: // sl@0: void buy_animal( animal* a ) sl@0: { sl@0: barn.push_back( a ); sl@0: } sl@0: sl@0: // sl@0: // The farm can also be in economical trouble and sl@0: // therefore be in the need to sell animals. sl@0: // sl@0: animal_transport sell_animal( animal_iterator to_sell ) sl@0: { sl@0: if( to_sell == end() ) sl@0: throw farm_trouble(); sl@0: sl@0: // sl@0: // Here we remove the animal from the barn, sl@0: // but the animal is not deleted yet...it's sl@0: // up to the buyer to decide what sl@0: // to do with it. sl@0: // sl@0: return barn.release( to_sell ); sl@0: } sl@0: sl@0: // sl@0: // How big a farm do we have? sl@0: // sl@0: size_type size() const sl@0: { sl@0: return barn.size(); sl@0: } sl@0: sl@0: // sl@0: // If things are bad, we might choose to sell all animals :-( sl@0: // sl@0: std::auto_ptr sell_farm() sl@0: { sl@0: return barn.release(); sl@0: } sl@0: sl@0: // sl@0: // However, if things are good, we might buy somebody sl@0: // else's farm :-) sl@0: // sl@0: sl@0: void buy_farm( std::auto_ptr other ) sl@0: { sl@0: // sl@0: // This line inserts all the animals from 'other' sl@0: // and is guaranteed either to succeed or to have no sl@0: // effect sl@0: // sl@0: barn.transfer( barn.end(), // insert new animals at the end sl@0: *other ); // we want to transfer all animals, sl@0: // so we use the whole container as argument sl@0: // sl@0: // You might think you would have to do sl@0: // sl@0: // other.release(); sl@0: // sl@0: // but '*other' is empty and can go out of scope as it wants sl@0: // sl@0: BOOST_ASSERT( other->empty() ); sl@0: } sl@0: sl@0: }; // class 'farm'. sl@0: sl@0: #include sl@0: int test_main(int,char *[]) sl@0: { sl@0: // sl@0: // First we make a farm sl@0: // sl@0: farm animal_farm; sl@0: BOOST_ASSERT( animal_farm.size() == 0u ); sl@0: sl@0: animal_farm.buy_animal( new pig("Betty") ); sl@0: animal_farm.buy_animal( new pig("Benny") ); sl@0: animal_farm.buy_animal( new pig("Jeltzin") ); sl@0: animal_farm.buy_animal( new cow("Hanz") ); sl@0: animal_farm.buy_animal( new cow("Mary") ); sl@0: animal_farm.buy_animal( new cow("Frederik") ); sl@0: BOOST_ASSERT( animal_farm.size() == 6u ); sl@0: sl@0: // sl@0: // Then we make another farm...it will actually contain sl@0: // a clone of the other farm. sl@0: // sl@0: farm new_farm( animal_farm.begin(), animal_farm.end() ); sl@0: BOOST_ASSERT( new_farm.size() == 6u ); sl@0: sl@0: // sl@0: // Is it really clones in the new farm? sl@0: // sl@0: BOOST_ASSERT( new_farm.begin()->name() == "Betty" ); sl@0: sl@0: // sl@0: // Then we search for an animal, Mary (the Crown Princess of Denmark), sl@0: // because we would like to buy her ... sl@0: // sl@0: typedef farm::animal_iterator iterator; sl@0: iterator to_sell; sl@0: for( iterator i = animal_farm.begin(), sl@0: end = animal_farm.end(); sl@0: i != end; ++i ) sl@0: { sl@0: if( i->name() == "Mary" ) sl@0: { sl@0: to_sell = i; sl@0: break; sl@0: } sl@0: } sl@0: sl@0: farm::animal_transport mary = animal_farm.sell_animal( to_sell ); sl@0: sl@0: sl@0: if( mary->speak() == muuuh ) sl@0: // sl@0: // Great, Mary is a cow, and she may live longer sl@0: // sl@0: new_farm.buy_animal( mary.release() ); sl@0: else sl@0: // sl@0: // Then the animal would be destroyed (!) sl@0: // when we go out of scope. sl@0: // sl@0: ; sl@0: sl@0: // sl@0: // Now we can observe some changes to the two farms... sl@0: // sl@0: BOOST_ASSERT( animal_farm.size() == 5u ); sl@0: BOOST_ASSERT( new_farm.size() == 7u ); sl@0: sl@0: // sl@0: // The new farm has however underestimated how much sl@0: // it cost to feed Mary and its owner is forced to sell the farm... sl@0: // sl@0: animal_farm.buy_farm( new_farm.sell_farm() ); sl@0: sl@0: BOOST_ASSERT( new_farm.size() == 0u ); sl@0: BOOST_ASSERT( animal_farm.size() == 12u ); sl@0: return 0; sl@0: }