views:

79

answers:

5

Does somebody have any idead on how to pass boost::shared_ptr - by value or by reference.

On my platform (32bit) sizeof(shared_ptr) equals 8 bytes and it looks like I should pass them by reference, but maybe somebody has another opinion / did a profile / something like that?

+1  A: 

I would pass a shared_ptr by reference-to-const. If you pass it by value, the use count is incremented by entering the function and decremented by leaving it, which is unnecessary overhead.

FredOverflow
If you do this, every access to the pointer is one more indirection away. The compiler won't be able cache the pointer value in a register, as the shared_ptr pointed to may change over time. The const keyword does not help, it does not guarantee other threads can change the object pointed to.
jdv
@jdv: The compiler is a single-thread beast. It doesn't care about other threads. If optimizations it does breaks multithread, that's your fault for writing poorly threaded code. And the compiler will happily destroy your threading hopes.
DeadMG
+2  A: 

I believe the essence of shared_ptr is that it is an object, and should be passed around by value. As I recall, it automatically handles reference counting using the ctor & dtor, so it knows when to free the pointer.

Whenever I've used it, I passed it by value.

James Curran
What exactly do you mean by "the essence of shared_ptr is that it is an object"?
FredOverflow
@Fred: It's reason for existing is that you could treat it as an object and not as a pointer.
James Curran
+5  A: 

In C++, it's generally a bad idea to choose whether to pass by value or reference based on the object's size.

Partly because the compiler will often perform copy elision on pass-by-value, negating the cost of copying the value, but mainly because the two options often behave differently.

So choose the option which bests expresses what you need to do.

With a shared_ptr, its entire reason for existing is that it can be copied, so that multiple objects can share ownership of the pointed-to object. If you never pass a shared_ptr by value, you can start wondering why it is a shared_ptr at all. A scoped_ptr may be a more efficient solution then.

Obviously, that's not to say you should always pass shared_ptr's by value either. Just that pass-by-value is a common use case for them.

If you need the caller and callee to have shared ownership, pass by value. If you don't want the callee to take any kind of ownership, pass by reference.

jalf
I've seen MT code performance suffering badly because copying smart pointers caused a lot of overheads for atomic increment and decrement of the reference counts. Changing pass-by-value to pass-by-reference in a few strategical places took performance from poor to good. Since then I think that maybe pass-by-reference should be the default.
sbi
@sbi: my point is just that if you consistently pass a `shared_ptr` around by reference, there's no point in it being a `shared_ptr`in the first place. Then you could get rid of the ref counter entirely. If you almost never want to increment/decrement the ref counter, you should consider why you're using a ref counted smart pointer at all.So perhaps my point isn't "default to pass by value", but rather "default to another smart pointer type, and fall back to `shared_ptr` when you need the ref counter (and then it is because you need to store multiple copies of it, so by value makes sense)
jalf
@jalf: That seems a good point, although I'd be hard pressed to think of a way to change the code in question according to it. Maybe if it was designed with that in mind...
sbi
+2  A: 

I'd call reference on a shared_ptr, where you can. This is because shared_ptr's operations are atomic, and thus unusually high overhead, as well as the potential to cache miss on the increment/decrement that simply isn't necessary if the calling function still has a strong reference to the object.

Of course, this isn't always possible- for example, within containers or in member vars, which need to hold their own reference.

And as another answerer said, the vast majority of any overhead in C++ is in the con/destructor, not passing the raw bits and bytes. This is true of shared_ptr, too.

DeadMG
+1  A: 

You can see this in two ways:

  • a boost::shared_ptr is an object (and should be passed by const &).

  • a boost::shared_ptr models a pointer and should be treated as a pointer.

Both of them are valid, and the second option will incur the cost of constructing and destructing the additional instance (which shouldn't be a big issue unless you're writing performance-critical code).

There are two cases when you should pass by value:

  • when you're not sure that the type of the pointer will remain the same. That is, if you are considering replacing the shared_ptr<T> with T* in your codebase somewhere in the future, it makes sense to write typedef shared_ptr<T> TPtr; and define your functions as void yourfunction(TPtr value)

    In this case, when you change the pointer type you will only have to modify the typedef line.

  • when you are sharing a pointer between two modules with a different lifetime. In that case, you need to make sure you have two instances of the smart pointer, and that both increment the reference count of the pointer.

In other cases, it's a matter of preference (unless you're writing performance-critical code in which case different rules apply).

utnapistim
Nice. Thank you and thank everyone.
HardCoder1986