1 | // |
---|
2 | // Boost.Pointer Container |
---|
3 | // |
---|
4 | // Copyright Thorsten Ottosen 2003-2005. Use, modification and |
---|
5 | // distribution is subject to the Boost Software License, Version |
---|
6 | // 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
---|
7 | // http://www.boost.org/LICENSE_1_0.txt) |
---|
8 | // |
---|
9 | // For more information, see http://www.boost.org/libs/ptr_container/ |
---|
10 | // |
---|
11 | |
---|
12 | // |
---|
13 | // This example is intended to get you started. |
---|
14 | // Notice how the smart container |
---|
15 | // |
---|
16 | // 1. takes ownership of objects |
---|
17 | // 2. transfers ownership |
---|
18 | // 3. applies indirection to iterators |
---|
19 | // 4. clones objects from other smart containers |
---|
20 | // |
---|
21 | |
---|
22 | // |
---|
23 | // First we select which container to use. |
---|
24 | // |
---|
25 | #include <boost/ptr_container/ptr_deque.hpp> |
---|
26 | |
---|
27 | // |
---|
28 | // we need these later in the example |
---|
29 | // |
---|
30 | #include <boost/assert.hpp> |
---|
31 | #include <string> |
---|
32 | #include <exception> |
---|
33 | |
---|
34 | |
---|
35 | // |
---|
36 | // Then we define a small polymorphic class |
---|
37 | // hierarchy. |
---|
38 | // |
---|
39 | |
---|
40 | class animal : boost::noncopyable |
---|
41 | { |
---|
42 | virtual std::string do_speak() const = 0; |
---|
43 | std::string name_; |
---|
44 | |
---|
45 | protected: |
---|
46 | // |
---|
47 | // Animals cannot be copied... |
---|
48 | // |
---|
49 | animal( const animal& r ) : name_( r.name_ ) { } |
---|
50 | void operator=( const animal& ); |
---|
51 | |
---|
52 | private: |
---|
53 | // |
---|
54 | // ...but due to advances in genetics, we can clone them! |
---|
55 | // |
---|
56 | |
---|
57 | virtual animal* do_clone() const = 0; |
---|
58 | |
---|
59 | public: |
---|
60 | animal( const std::string& name ) : name_(name) { } |
---|
61 | virtual ~animal() throw() { } |
---|
62 | |
---|
63 | std::string speak() const |
---|
64 | { |
---|
65 | return do_speak(); |
---|
66 | } |
---|
67 | |
---|
68 | std::string name() const |
---|
69 | { |
---|
70 | return name_; |
---|
71 | } |
---|
72 | |
---|
73 | animal* clone() const |
---|
74 | { |
---|
75 | return do_clone(); |
---|
76 | } |
---|
77 | }; |
---|
78 | |
---|
79 | // |
---|
80 | // An animal is still not Clonable. We need this last hook. |
---|
81 | // |
---|
82 | // Notice that we pass the animal by const reference |
---|
83 | // and return by pointer. |
---|
84 | // |
---|
85 | |
---|
86 | animal* new_clone( const animal& a ) |
---|
87 | { |
---|
88 | return a.clone(); |
---|
89 | } |
---|
90 | |
---|
91 | // |
---|
92 | // We do not need to define 'delete_clone()' since |
---|
93 | // since the default is to call the default 'operator delete()'. |
---|
94 | // |
---|
95 | |
---|
96 | const std::string muuuh = "Muuuh!"; |
---|
97 | const std::string oiink = "Oiiink"; |
---|
98 | |
---|
99 | class cow : public animal |
---|
100 | { |
---|
101 | virtual std::string do_speak() const |
---|
102 | { |
---|
103 | return muuuh; |
---|
104 | } |
---|
105 | |
---|
106 | virtual animal* do_clone() const |
---|
107 | { |
---|
108 | return new cow( *this ); |
---|
109 | } |
---|
110 | |
---|
111 | public: |
---|
112 | cow( const std::string& name ) : animal(name) { } |
---|
113 | }; |
---|
114 | |
---|
115 | class pig : public animal |
---|
116 | { |
---|
117 | virtual std::string do_speak() const |
---|
118 | { |
---|
119 | return oiink; |
---|
120 | } |
---|
121 | |
---|
122 | virtual animal* do_clone() const |
---|
123 | { |
---|
124 | return new pig( *this ); |
---|
125 | } |
---|
126 | |
---|
127 | public: |
---|
128 | pig( const std::string& name ) : animal(name) { } |
---|
129 | }; |
---|
130 | |
---|
131 | // |
---|
132 | // Then we, of course, need a place to put all |
---|
133 | // those animals. |
---|
134 | // |
---|
135 | |
---|
136 | class farm |
---|
137 | { |
---|
138 | // |
---|
139 | // This is where the smart containers are handy |
---|
140 | // |
---|
141 | typedef boost::ptr_deque<animal> barn_type; |
---|
142 | barn_type barn; |
---|
143 | |
---|
144 | // |
---|
145 | // An error type |
---|
146 | // |
---|
147 | struct farm_trouble : public std::exception { }; |
---|
148 | |
---|
149 | public: |
---|
150 | // |
---|
151 | // We would like to make it possible to |
---|
152 | // iterate over the animals in the farm |
---|
153 | // |
---|
154 | typedef barn_type::iterator animal_iterator; |
---|
155 | |
---|
156 | // |
---|
157 | // We also need to count the farm's size... |
---|
158 | // |
---|
159 | typedef barn_type::size_type size_type; |
---|
160 | |
---|
161 | // |
---|
162 | // And we also want to transfer an animal |
---|
163 | // safely around. The easiest way to think |
---|
164 | // about '::auto_type' is to imagine a simplified |
---|
165 | // 'std::auto_ptr<T>' ... this means you can expect |
---|
166 | // |
---|
167 | // T* operator->() |
---|
168 | // T* release() |
---|
169 | // deleting destructor |
---|
170 | // |
---|
171 | // but not more. |
---|
172 | // |
---|
173 | typedef barn_type::auto_type animal_transport; |
---|
174 | |
---|
175 | // |
---|
176 | // Create an empty farm. |
---|
177 | // |
---|
178 | farm() { } |
---|
179 | |
---|
180 | // |
---|
181 | // We need a constructor that can make a new |
---|
182 | // farm by cloning a range of animals. |
---|
183 | // |
---|
184 | farm( animal_iterator begin, animal_iterator end ) |
---|
185 | : |
---|
186 | // |
---|
187 | // Objects are always cloned before insertion |
---|
188 | // unless we explicitly add a pointer or |
---|
189 | // use 'release()'. Therefore we actually |
---|
190 | // clone all animals in the range |
---|
191 | // |
---|
192 | barn( begin, end ) { } |
---|
193 | |
---|
194 | // |
---|
195 | // ... so we need some other function too |
---|
196 | // |
---|
197 | |
---|
198 | animal_iterator begin() |
---|
199 | { |
---|
200 | return barn.begin(); |
---|
201 | } |
---|
202 | |
---|
203 | animal_iterator end() |
---|
204 | { |
---|
205 | return barn.end(); |
---|
206 | } |
---|
207 | |
---|
208 | // |
---|
209 | // Here it is quite ok to have an 'animal*' argument. |
---|
210 | // The smart container will handle all ownership |
---|
211 | // issues. |
---|
212 | // |
---|
213 | void buy_animal( animal* a ) |
---|
214 | { |
---|
215 | barn.push_back( a ); |
---|
216 | } |
---|
217 | |
---|
218 | // |
---|
219 | // The farm can also be in economical trouble and |
---|
220 | // therefore be in the need to sell animals. |
---|
221 | // |
---|
222 | animal_transport sell_animal( animal_iterator to_sell ) |
---|
223 | { |
---|
224 | if( to_sell == end() ) |
---|
225 | throw farm_trouble(); |
---|
226 | |
---|
227 | // |
---|
228 | // Here we remove the animal from the barn, |
---|
229 | // but the animal is not deleted yet...it's |
---|
230 | // up to the buyer to decide what |
---|
231 | // to do with it. |
---|
232 | // |
---|
233 | return barn.release( to_sell ); |
---|
234 | } |
---|
235 | |
---|
236 | // |
---|
237 | // How big a farm do we have? |
---|
238 | // |
---|
239 | size_type size() const |
---|
240 | { |
---|
241 | return barn.size(); |
---|
242 | } |
---|
243 | |
---|
244 | // |
---|
245 | // If things are bad, we might choose to sell all animals :-( |
---|
246 | // |
---|
247 | std::auto_ptr<barn_type> sell_farm() |
---|
248 | { |
---|
249 | return barn.release(); |
---|
250 | } |
---|
251 | |
---|
252 | // |
---|
253 | // However, if things are good, we might buy somebody |
---|
254 | // else's farm :-) |
---|
255 | // |
---|
256 | |
---|
257 | void buy_farm( std::auto_ptr<barn_type> other ) |
---|
258 | { |
---|
259 | // |
---|
260 | // This line inserts all the animals from 'other' |
---|
261 | // and is guaranteed either to succeed or to have no |
---|
262 | // effect |
---|
263 | // |
---|
264 | barn.transfer( barn.end(), // insert new animals at the end |
---|
265 | *other ); // we want to transfer all animals, |
---|
266 | // so we use the whole container as argument |
---|
267 | // |
---|
268 | // You might think you would have to do |
---|
269 | // |
---|
270 | // other.release(); |
---|
271 | // |
---|
272 | // but '*other' is empty and can go out of scope as it wants |
---|
273 | // |
---|
274 | BOOST_ASSERT( other->empty() ); |
---|
275 | } |
---|
276 | |
---|
277 | }; // class 'farm'. |
---|
278 | |
---|
279 | int main() |
---|
280 | { |
---|
281 | // |
---|
282 | // First we make a farm |
---|
283 | // |
---|
284 | farm animal_farm; |
---|
285 | BOOST_ASSERT( animal_farm.size() == 0u ); |
---|
286 | |
---|
287 | animal_farm.buy_animal( new pig("Betty") ); |
---|
288 | animal_farm.buy_animal( new pig("Benny") ); |
---|
289 | animal_farm.buy_animal( new pig("Jeltzin") ); |
---|
290 | animal_farm.buy_animal( new cow("Hanz") ); |
---|
291 | animal_farm.buy_animal( new cow("Mary") ); |
---|
292 | animal_farm.buy_animal( new cow("Frederik") ); |
---|
293 | BOOST_ASSERT( animal_farm.size() == 6u ); |
---|
294 | |
---|
295 | // |
---|
296 | // Then we make another farm...it will actually contain |
---|
297 | // a clone of the other farm. |
---|
298 | // |
---|
299 | farm new_farm( animal_farm.begin(), animal_farm.end() ); |
---|
300 | BOOST_ASSERT( new_farm.size() == 6u ); |
---|
301 | |
---|
302 | // |
---|
303 | // Is it really clones in the new farm? |
---|
304 | // |
---|
305 | BOOST_ASSERT( new_farm.begin()->name() == "Betty" ); |
---|
306 | |
---|
307 | // |
---|
308 | // Then we search for an animal, Mary (the Crown Princess of Denmark), |
---|
309 | // because we would like to buy her ... |
---|
310 | // |
---|
311 | typedef farm::animal_iterator iterator; |
---|
312 | iterator to_sell; |
---|
313 | for( iterator i = animal_farm.begin(), |
---|
314 | end = animal_farm.end(); |
---|
315 | i != end; ++i ) |
---|
316 | { |
---|
317 | if( i->name() == "Mary" ) |
---|
318 | { |
---|
319 | to_sell = i; |
---|
320 | break; |
---|
321 | } |
---|
322 | } |
---|
323 | |
---|
324 | farm::animal_transport mary = animal_farm.sell_animal( to_sell ); |
---|
325 | |
---|
326 | |
---|
327 | if( mary->speak() == muuuh ) |
---|
328 | // |
---|
329 | // Great, Mary is a cow, and she may live longer |
---|
330 | // |
---|
331 | new_farm.buy_animal( mary.release() ); |
---|
332 | else |
---|
333 | // |
---|
334 | // Then the animal would be destroyed (!) |
---|
335 | // when we go out of scope. |
---|
336 | // |
---|
337 | ; |
---|
338 | |
---|
339 | // |
---|
340 | // Now we can observe some changes to the two farms... |
---|
341 | // |
---|
342 | BOOST_ASSERT( animal_farm.size() == 5u ); |
---|
343 | BOOST_ASSERT( new_farm.size() == 7u ); |
---|
344 | |
---|
345 | // |
---|
346 | // The new farm has however underestimated how much |
---|
347 | // it cost to feed Mary and its owner is forced to sell the farm... |
---|
348 | // |
---|
349 | animal_farm.buy_farm( new_farm.sell_farm() ); |
---|
350 | |
---|
351 | BOOST_ASSERT( new_farm.size() == 0u ); |
---|
352 | BOOST_ASSERT( animal_farm.size() == 12u ); |
---|
353 | } |
---|