Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are potential dangers when using boost::shared_ptr?

What are some ways you can shoot yourself in the foot when using boost::shared_ptr? In other words, what pitfalls do I have to avoid when I use boost::shared_ptr?

like image 314
Frank Avatar asked Mar 31 '09 15:03

Frank


People also ask

Is boost :: shared_ptr thread safe?

boost::shared_ptr<> offers a certain level of thread safety. The reference count is manipulated in a thread safe manner (unless you configure boost to disable threading support). So you can copy a shared_ptr around and the ref_count is maintained correctly.

What is boost :: shared_ptr?

shared_ptr is now part of the C++11 Standard, as std::shared_ptr . Starting with Boost release 1.53, shared_ptr can be used to hold a pointer to a dynamically allocated array. This is accomplished by using an array type ( T[] or T[N] ) as the template parameter.

What happens when shared_ptr goes out of scope?

The smart pointer has an internal counter which is decreased each time that a std::shared_ptr , pointing to the same resource, goes out of scope – this technique is called reference counting. When the last shared pointer is destroyed, the counter goes to zero, and the memory is deallocated.

Should you use shared_ptr?

An object referenced by the contained raw pointer will not be destroyed until reference count is greater than zero i.e. until all copies of shared_ptr have been deleted. So, we should use shared_ptr when we want to assign one raw pointer to multiple owners. // referring to the same managed object.


2 Answers

Cyclic references: a shared_ptr<> to something that has a shared_ptr<> to the original object. You can use weak_ptr<> to break this cycle, of course.


I add the following as an example of what I am talking about in the comments.

class node : public enable_shared_from_this<node> { public :     void set_parent(shared_ptr<node> parent) { parent_ = parent; }     void add_child(shared_ptr<node> child) {         children_.push_back(child);         child->set_parent(shared_from_this());     }      void frob() {         do_frob();         if (parent_) parent_->frob();     }  private :     void do_frob();     shared_ptr<node> parent_;     vector< shared_ptr<node> > children_; }; 

In this example, you have a tree of nodes, each of which holds a pointer to its parent. The frob() member function, for whatever reason, ripples upwards through the tree. (This is not entirely outlandish; some GUI frameworks work this way).

The problem is that, if you lose reference to the topmost node, then the topmost node still holds strong references to its children, and all its children also hold a strong reference to their parents. This means that there are circular references keeping all the instances from cleaning themselves up, while there is no way of actually reaching the tree from the code, this memory leaks.

class node : public enable_shared_from_this<node> { public :     void set_parent(shared_ptr<node> parent) { parent_ = parent; }     void add_child(shared_ptr<node> child) {         children_.push_back(child);         child->set_parent(shared_from_this());     }      void frob() {         do_frob();         shared_ptr<node> parent = parent_.lock(); // Note: parent_.lock()         if (parent) parent->frob();     }  private :     void do_frob();     weak_ptr<node> parent_; // Note: now a weak_ptr<>     vector< shared_ptr<node> > children_; }; 

Here, the parent node has been replaced by a weak pointer. It no longer has a say in the lifetime of the node to which it refers. Thus, if the topmost node goes out of scope as in the previous example, then while it holds strong references to its children, its children don't hold strong references to their parents. Thus there are no strong references to the object, and it cleans itself up. In turn, this causes the children to lose their one strong reference, which causes them to clean up, and so on. In short, this wont leak. And just by strategically replacing a shared_ptr<> with a weak_ptr<>.

Note: The above applies equally to std::shared_ptr<> and std::weak_ptr<> as it does to boost::shared_ptr<> and boost::weak_ptr<>.

like image 76
Kaz Dragon Avatar answered Sep 23 '22 00:09

Kaz Dragon


Creating multiple unrelated shared_ptr's to the same object:

#include <stdio.h> #include "boost/shared_ptr.hpp"  class foo { public:     foo() { printf( "foo()\n"); }      ~foo() { printf( "~foo()\n"); } };  typedef boost::shared_ptr<foo> pFoo_t;  void doSomething( pFoo_t p) {     printf( "doing something...\n"); }  void doSomethingElse( pFoo_t p) {     printf( "doing something else...\n"); }  int main() {     foo* pFoo = new foo;      doSomething( pFoo_t( pFoo));     doSomethingElse( pFoo_t( pFoo));      return 0; } 
like image 37
Michael Burr Avatar answered Sep 20 '22 00:09

Michael Burr