views:

803

answers:

5

I have few questions on the best practices of using shared_ptr.

Question 1

Is copying shared_ptr cheap? Or do I need to pass it as reference to my own helper functions and return as value? Something like,

void init_fields(boost::shared_ptr<foo>& /*p_foo*/);
void init_other_fields(boost::shared_ptr<foo>& /*p_foo*/);

boost::shared_ptr<foo> create_foo()
{
    boost::shared_ptr<foo> p_foo(new foo);
    init_fields(p_foo);
    init_other_fields(p_foo);
}

Question 2

Should I use boost::make_shared to construct a shared_ptr? If yes, what advantages it offers? And how can we use make_shared when T doesn't have a parameter-less constructor?

Question 3

How to use const foo*? I have found two approaches for doing this.

void take_const_foo(const foo* pfoo)
{

}

int main()
{
    boost::shared_ptr<foo> pfoo(new foo);
    take_const_foo(pfoo.get());
    return 0;
}

OR

typedef boost::shared_ptr<foo> p_foo;
typedef const boost::shared_ptr<const foo> const_p_foo;

void take_const_foo(const_p_foo pfoo)
{

}

int main()
{
     boost::shared_ptr<foo> pfoo(new foo);
     take_const_foo(pfoo);
     return 0;
}

Question 4

How can I return and check for NULL on a shared_ptr object? Is it something like,

boost::shared_ptr<foo> get_foo()
{
     boost::shared_ptr<foo> null_foo;
     return null_foo;
}

int main()
{
     boost::shared_ptr<foo> f = get_foo();
     if(f == NULL)
     {
          /* .. */
     }
     return 0;
}

Any help would be great.

+2  A: 
  1. One of the basic reasons for the existence of shared_ptr is to be relatively cheap to copy.
  2. There are versions of make_shared that take parameters (and if your compiler supports variadic templates, one that takes a variable parameter list).
  3. It sounds like you're looking for const_ptr_cast?
  4. To return a null pointer, you can pass '0' to the share_ptr ctor. To check for a null pointer, you can compare p.get() to 0.
Jerry Coffin
re #4: Doesn't `if(f)`/`if(!f)` work?
sbi
for #4: you can compare `p` with 0 as well actually
laura
@sbi and laura: yes, it's true that it has conversions built in, so most of the time you can treat it as if it was a normal pointer.
Jerry Coffin
+2  A: 
  1. Yes, the copy is absolutely cheap. Besides holding the pointer, there is (usually) one other data member for the shared_ptr class - the use count.
  2. Can't answer this, I generally use boost versions before make_shared was introduced (1.40?)
  3. Use boost::const_pointer_cast
  4. shared_ptr has operator==/!= defined. In your example above: if (f)
smt
+1  A: 
  1. Copying is cheap, the pointer doesn't take much space. The whole point of it was to make it small to allow usage in containers by value ( e.g. std::vector< shared_ptr<Foo> > ).

  2. make_shared takes a variable amount of parameters, and is the prefered mechanicsm over constructing it yourself (just like make_pair). The advantage is readability, especially if passing temporaries and/or namespaces is involved:

  3. boost::const_ptr_cast as already suggested

  4. smart pointers have overloaded operators and may be directly used in expressions evaluated to bool. Don't use get. For anything. Instead of comparing p.get to anything, compare a empty pointer instance ( my_ptr != boost::shared_ptr< MyClass >() )

AD.2

func_shared( boost::shared_ptr<my_tools::MyLongNamedClass>( 
    new my_tools::MyLongNamedClass( param1, param2 ) );

versus

func_shared( boost::make_shared<my_tools::MyLongNamedClass>( param1, param2 ));
Kornel Kisielewicz
+5  A: 

Most of the questions have been answered, but I disagree that a shared_ptr copy is cheap.

A copy has different semantics from a pass-by-reference. It will modify the reference count, which will trigger an atomic increment in the best case and a lock in the worst case. You must decide what semantics you need and then you will know whether to pass by reference or by value.

From a performance point of view, it's usually a better idea to use a boost pointer container instead of a container of shared_ptr.

rpg
Yep, you're right, copying ref-counted pointers can be expensive! +1
sbi
+3  A: 
  1. Copying a shared_ptr now costs 32 bytes in stack copy and extra refcount increments/decrements. Decide whether it is cheap for you or not, but I see no reason why not to pass a const reference, especially that you already have a typedef for the ptr: void f(const foo_ptr &myfoo) especially given that the standard no-write-permissions parameter passing in C++ is const reference.

  2. I would prefer having no functions that accept pointer that is not shared. This is similar (though not identical) to parameter passing semantics in Java and C#. Why dive into deciding every time how to pass an object, instead of having one standard way of doing it?

  3. Use if(p) just as for regular pointers. The boolean conversion semantics is pretty neat.

Pavel Radzivilovsky