When passing objects into functions, do the same rules apply to smart pointers as to other objects that contain dynamic memory?
When I pass, for example, a std::vector<std::string> into a function I always consider the following options:
I'm going to change the state of the vector object, but I do not want those changes reflected after the function has finished, AKA make a copy.
void function(std::vector<std::string> vec);
I'm going to change the state of the vector object, and I do want those changes reflected after the function has finished, AKA make a reference.
void function(std::vector<std::string> & vec);
This object is pretty big, so I'd better pass a reference, but tell the compiler not to let me change it.
void function(std::vector<std::string> const& vec);
Now is this the same logic with smart pointers? And when should I consider move semantics? Some guidelines on how I should pass smart pointers is what I desire most.
Smart pointers have pointer semantics, not value semantics (well, not the way you mean it). Think of shared_ptr<T> as a T*; treat it as such (well, except for the reference counting and automatic deletion). Copying a smart pointer does not copy the object it points to, just like copying a T* does not copy the T it points to.
You can't copy a unique_ptr at all. The whole point of the class is that it cannot be copied; if it could, then it wouldn't be a unique (ie: singular) pointer to an object. You have to either pass it by some form of reference or by moving it.
Smart pointers are all about ownership of what they point to. Who owns this memory and who will be responsible for deleting it. unique_ptr represents unique ownership: exactly one piece of code owns this memory. You can transfer ownership (via move), but in so doing, you lose ownership of the memory. shared_ptr represents shared ownership.
In all cases, the use of a smart pointer in a parameter list represents transferring ownership. Therefore, if a function takes a smart pointer, then it is going to claim ownership of that object. If a function isn't supposed to take ownership, then it shouldn't be taking a smart pointer at all; use a reference (T&) or if you have need of nullability, a pointer but never store it.
If you are passing someone a unique_ptr, you are giving them ownership. Which means, by the nature of unique ownership, you are losing ownership of the memory. Thus, there's almost no reason to ever pass a unique_ptr by anything except by value.
Similarly, if you want to share ownership of some object, you pass in a shared_ptr. Whether you do it by reference or by value is up to you. Since you're sharing ownership, it's going to make a copy anyway (presumably), so you might as well take it by value. The function can use std::move to move it into class members or the like.
If the function isn't going to modify or make a copy of the pointer, just use a dumb pointer instead. Smart pointers are used to control the lifetime of an object, but the function isn't going to change the lifetime so it doesn't need a smart pointer, and using a dumb pointer gives you some flexibility in the type used by the caller.
void function(std::string * ptr);
function(my_unique_ptr.get());
function(my_shared_ptr.get());
function(my_dumb_ptr);
unique_ptr can't be copied without invalidating the original, so if you must pass it you must pass a reference.
For a more in-depth look at this recommendation by someone a lot smarter than me, see Herb Sutter's GotW #91 Solution: Smart Pointer Parameters. He goes beyond this recommendation and suggests that if the pointer can't be null, you should pass by reference instead of pointer. This requires dereferencing the pointer at the site of the call.
void function(std::string & val);
assert(my_unique_ptr != nullptr && my_shared_ptr != nullptr && my_dumb_ptr != nullptr);
function(*my_unique_ptr);
function(*my_shared_ptr);
function(*my_dumb_ptr);
A smart pointer is an object that refer to another object an manages its lifetime.
Passing a smart pointer reuquires to respect the semantics the smart poitner support:
Passing as const smartptr<T>& always work (and you cannot change the pointer, but can change the state of what it points to).
Passing as smartptr<T>& always work (and you can change the pointer as well).
Passing as smartptr<T> (by copy) works only if smartptr is copyable. It works with std::shared_ptr, but not with std::unique_ptr, unless you "move" it on call, like in func(atd::move(myptr)), thus nullifying myptr, moving the pointer to the passed parameter. (Note that move is implicit if myptr is temporary).
Passing as smartptr<T>&& (by move) imposes the pointer to be moved on call, by forcing you to explicitly use std::move (but requires "move" to make sense for the particular pointer).
I know the person asked this question is more knowledgeable than me in C++ and There are some perfect answer to this question but I believe This question better be answer in a way that it doesn't scares people from C++, although it could get a little complicated, and this is my try:
Consider there is only one type of smart pointer in C++ and it is shared_ptr, so we have these options to pass it to a function:
1 - by value : void f(std::shared_ptr<Object>);
2 - by reference : void f(std::shared_ptr<Object>&);
The biggest difference is ,the First one lend you the ownership and The second one let you manipulate the ownership.
further reading and details could be at this link which helped me before.
Related
The CppCoreGuidlines rule R.33 suggests to
Take a unique_ptr<widget>& parameter to express that a function
reseats the widget.
Reason Using unique_ptr in this way both documents and enforces the
function call’s reseating semantics.
Note “reseat” means “making a pointer or a smart pointer refer to a
different object.”
I don't understand why we should pass by reference when reseat means "making a pointer or a smart pointer refer to a different object.”
When the function's purpose is to reseat/change the underlying object the pointer is pointing to, aren't we stealing the ownership from the caller this way and therefore should pass the unique_ptr by value, hence moving it and transferring ownership?
Is there an example that explains why passing a unique_ptr by reference is recommended?
When the function's purpose is to reseat/change the underlying object the pointer is pointing to, aren't we stealing the ownership from the caller this way
No. Neither when we "reseat" a pointer, nor when we change the pointed object, do we take ownership of the pointer i.e. we aren't transferring the ownership.
Is there an example that explains why passing a unique_ptr by reference is recommended?
Here is an example of a function that "reseats" a unique pointer:
void reseat(std::unique_ptr<widget>& ptr) {
ptr = std::make_unique<widget>();
}
If you tried to use a reference to const, then that wouldn't compile at all. If you tried to use a non-reference parameter, then the argument pointer wouldn't be modified and thus the behaviour wouldn't be as desired. The caller would be forced to move their pointer leaving it always null.
You could modify the example to use a pointer to unique pointer, but references are recommended because it isn't possible to pass null by mistake. A reference wrapper would also work, but it would be unnecessarily complicated.
In case we make it point somewhere else, what happens to the object it pointed before?
If a unique pointer points to something other than null, then making it point elsewhere will cause the previously pointed object to be deleted.
Aren't we leaking memory in this case?
No.
Note that the example is simple in order to be easy to understand. Typically I wouldn't recommend to write such function and instead considering to write a function that returns the new unique pointer, and let the caller "reseat" the pointer themselves. But that depends on details.
Someone made question "should I pass shared_ptr by reference" and he got this reply which has plenty upvotes. https://stackoverflow.com/a/8385731/5543597
It makes me wonder why he has so many upvotes, and if it's true what he is saying:
That depends on what you want. Should the callee share ownership of
the object? Then it needs its own copy of the shared_ptr. So pass it
by value.
If a function simply needs to access an object owned by the caller, go
ahead and pass by (const) reference, to avoid the overhead of copying
the shared_ptr.
The best practice in C++ is always to have clearly defined ownership
semantics for your objects. There is no universal "always do this" to
replace actual thought.
If you always pass shared pointers by value, it gets costly (because
they're a lot more expensive to copy than a raw pointer). If you never
do it, then there's no point in using a shared pointer in the first
place.
Copy the shared pointer when a new function or object needs to share
ownership of the pointee.
Especially here:
Should the callee share ownership of the object? Then it needs its own
copy of the shared_ptr. So pass it by value.
Why creating copy of shared_ptr by passing by value, when it could be reference and callee could just make copy of shared_ptr using reference he received, once he decide to store it in his data?
Or this:
If you never do it, then there's no point in using a shared pointer in
the first place.
When passing it by refence to functions, it still exists in parent function. And once decided to store it, it can be stored without problems.
Are these statements correct?
Yes that answer is correct.
Why creating copy of shared_ptr by passing by value, when it could be reference and callee could just make copy of shared_ptr using reference he received, once he decide to store it in his data?
You could do that. But passing by value serves as self-documentation that the callee might will make a copy. Also, passing by value has the advantage that the function can be called with an rvalue and moved out of, e.g. func( make_shared<T>() );
std::unique_ptr is a smart pointer that retains sole ownership of an object through a pointer and destroys that object when the unique_ptr goes out of scope. No two unique_ptr instances can manage the same object.
How the last statement is ensured?
I don't believe that there is "someone" is STL who checks if one of the already existing std::unique_ptrs already own the raw pointer. This would be very inefficient with huge number of unique pointers, even if it is a linear complexity algorithm. There should be a nice trick, right?
It isn't ensured. The name is a statement of intended usage, not any guarantee fully enforced by a runtime system. That is, you can write this code:
std::unique_ptr<int> i1(new int());
std::unique_ptr<int> i2(i1.get());
and you have two unique_ptrs referring to the same object, but the program has undefined behavior because it will delete the pointer twice.
unique_ptr is not copyable to make it harder to create two such pointers by accident. C++ protects against Murphy, not Machiavelli.
The reason it's called unique is because you can't copy a unique pointer. You can steal its value, but that leaves the original unique_ptr empty.
Smart-pointers are generally tiny so passing by value isn't a problem, but is there any problem passing references to them; or rather are there specific cases where this mustn't be done?
I'm writing a wrapper library and several of my classes wrap smart-pointer objects in the underlying library... my classes are not smart-pointers but the APIs currently pass smart-pointer objects by value.
e.g current code:
void class::method(const AnimalPtr pAnimal) { ... }
becomes
void class::method(const MyAnimal &animal){...}
where MyAnimal is my new wrapper class encapsulating AnimalPtr.
There is no guarantee the Wrapper classes won't one day grow beyond wrapping a smart-pointer, so passing by value makes me nervous.
You should pass shared pointers by reference, not value, in most cases. While the size of a std::shared_ptr is small, the cost of copying involves an atomic operation (conceptually an atomic increment and an atomic decrement on destruction of the copy, although I believe that some implementations manage to do a non-atomic increment).
In other cases, for example std::unique_ptr you might prefer to pass by value, as the copy will have to be a move and it clearly documents that ownership of the object is transferred to the function (if you don't want to transfer ownership, then pass a reference to the real object, not the std::unique_ptr).
In other cases your mileage might vary. You need to be aware of what the semantics of copy are for your smart pointer, and whether you need to pay for the cost or not.
It's ok to pass a smart pointer by reference, except if it's to a constructor. In a constructor, it's possible to store a reference to the original object, which violates the contract of the smart pointers. You would likely get memory corruption if you did that. Even if your constructor does not today store the reference, I would still be wary because code changes and it's an easy thing to miss if you decide later you need to hold the variable longer.
In a normal function, you cannot store a function parameter as a reference anywhere because references must be set during their initialization. You could assign the reference to some longer-living non-reference variable, but that would be a copy and so would increase its lifetime appropriately. So in either case, you could not hold onto it past when the calling function might have freed it. In this case, you might get a small performance boost with a reference, but I wouldn't plan on noticing it in most cases.
So I would say - constructor, always pass by value; other functions, pass by reference if you want.
In the following example, when I pass p to a function, It gets destroyed as soon as the function func exits
void func(std::auto_ptr<int> p)
{
// deletes p
}
int main()
{
std::auto_ptr<int> p(new int);
func(p);
*p = 1; // run-time error
}
I'm also told that passing smart pointers by reference is very bad design from the book "The C++ Standard Library - Reference by Nicolai M. Josuttis".
Quote:
Allowing an auto_ptr to pass by reference is very bad design and you
should always avoid it .....
..... According to the concept of auto_ptrs, it is possible to transfer ownership into a function by using a constant reference.
Is it not possible to pass smart pointers or have I got the wrong idea?
Is it not possible to pass smart pointers or have I got the wrong idea?
It only applies to auto_ptr. Also, as per the new C++11 standard auto_ptr is deprecated and unique_ptr is the superior alternative if you are using c++11.
The auto_ptr template class ensures that the object to which it points gets destroyed automatically when control leaves a scope, If you pass auto_ptr by value in a function, the object is deleted once the scope of the function ends. So essentially you transfer the ownership of the pointer to the function and you don't own the pointer beyond the function call.
Passing an auto_ptr by reference is considered a bad design because auto_ptr was specifically designed for transfer of ownership and passing it by reference means that the function may or may not take over the ownership of the passed pointer.
In case of unique_ptr, If you pass an unique_ptr to function by value then you are transferring the ownership of the unique_ptr to the function.
In case you are passing a reference of unique_ptr to the function if, You just want the function to use the pointer but you do not want to pass it's ownership to the function.
shared_ptr operates on a reference counting mechanism, so the count is always incremented when copying functions are called and decremented when a call is made to destructor.
Passing a shared_ptr by reference avoids calls to either and hence it can be passed by reference. While passing it by value appropriately increments and decrements the count, the copy constructor for shared_ptr is not very expensive for most cases but it might matter in some scenarios, So using either of the two depends on the situation.
You cannot pass a std::auto_ptr, but you can pass a smart pointer that manages the number of references to it, like boost::shared_ptr.