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