4

I've recently started using boost. So far most things have been pretty straight forward. But one thing that is driving me nuts is the proliferation of shared_ptr throughout boost. Even in trivial examples, shared_ptr is used.

So my question is, if I am using boost for accepting tcp connections and then handling them. As long as I guarantee that the objects created on the heap (the boost::asio::ip::tcp::socket, and the class that will be called back for async methods) will not be deleted until I am done using tcp, then I don't need shared_ptr correct?

I've written a simple tcp server and client, not using shared ptr, it works. But I'd just like some outside confirmation that my assessment is correct.

Also, in your experience have you ever had a need to use shared_ptr to appease boost?

Sam Miller
  • 23,808
  • 4
  • 67
  • 87
anio
  • 8,903
  • 7
  • 35
  • 53
  • If you upload your code somewhere, perhaps as a github gist (http://gist.github.com), it would be easier to comment on your attempt. – Daniel Lidström Nov 04 '10 at 19:01
  • @Daniel: Thanks for pointing out gist.github.com, never knew of it. I'll put something up when I get a chance. – anio Nov 05 '10 at 14:59

3 Answers3

7

Read the documentation for the io_service destructor

The destruction sequence described above permits programs to simplify their resource management by using shared_ptr<>. Where an object's lifetime is tied to the lifetime of a connection (or some other sequence of asynchronous operations), a shared_ptr to the object would be bound into the handlers for all asynchronous operations associated with it. This works as follows:

When a single connection ends, all associated asynchronous operations complete. The corresponding handler objects are destroyed, and all shared_ptr references to the objects are destroyed.

To shut down the whole program, the io_service function stop() is called to terminate any run() calls as soon as possible. The io_service destructor defined above destroys all handlers, causing all shared_ptr references to all connection objects to be destroyed.

in other words, it will be exponentially easier to use a shared_ptr instead of naked pointers.

Sam Miller
  • 23,808
  • 4
  • 67
  • 87
  • 1
    I have little experience with asio, but it seems that if io_service had a function "stop_and_delete_me" that could be called from a handler, then I don't see that this shared_ptr thing would be an issue. Then it could be shutdown easily like this io_service.post([&](){ cleanup_my_resources(); io_service.stop_and_delete_me() }); Since cleanup of resources is done in a handler, there is no issue. Since stop_and_delete_me guarantees that no more handlers are invoked, that's no issue either. The huge advantage of this is that the code is deterministic. shared_ptr is not deterministic. – user239558 Mar 14 '13 at 11:59
0

shared_ptr, or something like it (vector, auto_ptr, etc) is required to maintain exception safety. The instant you put a delete call into your code, an exception can be thrown which would cause the delete to get skipped, leaking memory.

If you're using all stack allocated objects, and can get away with doing so, by all means do so. I would guess the reason for most of the shared_ptrs you are seeing are because somebody wants to store an object with polymorphic behavior, and doesn't want to be subject to the slicing problem.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • Huh? Last time I checked delete doesn't throw an exception. I've never had that happen to me. Maybe if you try deleting garbage things will explode. Supposing delete did throw an exception, shared_ptr would then blow up when trying to delete when reference count reaches zero. But, as I have stated, I am managing the memory myself, and doing it properly. Also, I don't understand what you mean when you say that a shared_ptr avoids the slicing problem. How? – anio Nov 05 '10 at 12:43
  • @anio: No -- the `delete` itself isn't what causes the problem. The problem is that an exception can be thrown at any time between the call to `new` and the call to `delete`. When the exception is thrown (between the new and delete calls), the call to `delete` never happens, and therefore you've leaked memory. Putting the pointer into some form of controlled container ensures the memory is `delete` ed when the stack is unwound in the event of a thrown exception. – Billy ONeal Nov 05 '10 at 12:59
0

Reference counting is inherent to performing asynchronous operations. Otherwise there is no way to know when an object is no longer in use. You could avoid shared_ptr by maintaining your own reference count. But you would be substantially reimplementing the same thing. The problem here is not shared_ptr but your attitude towards it. You should simply accept that reference counting is necessary for asynchronous code, therefore shared_ptr is a natural solution.

Vinnie Falco
  • 5,173
  • 28
  • 43