I've been away from serious C++ for about ten years. I'm coming back in to the fold and am currently working on a project to fully familiarize myself with C++11. I'm having a bit of an existential crisis about how to best pass std::shared_ptr's around.
For a simple example, take the following setup:
class ServiceB {
public:
ServiceB() {}
};
class ServiceA {
public:
ServiceA(std::shared_ptr<ServiceB>& serviceB)
: _serviceB(serviceB) {
}
private:
std::shared_ptr<ServiceB> _serviceB;
};
class Root {
public:
Root()
: _serviceB(std::shared_ptr<ServiceB>(new ServiceB())),
_serviceA(std::unique_ptr<ServiceA>(new ServiceA(_serviceB))) {
}
private:
std::shared_ptr<ServiceB> _serviceB;
std::unique_ptr<ServiceA> _serviceA;
};
Notice here that ServiceA requires a reference to ServiceB. I'd like to keep that reference tied up in a shared_ptr. Am I okay to do what I did here, which is simply pass the shared_ptr down as a reference and let the std::shared_ptr copy constructor do the work for me? Does this properly increment the reference count on the shared_ptr?
If this is not the best way to do this, what is the common "best practice" for passing around std::shared_ptr's?
In general, you should pass the shared pointer as a straight copy. This gives it its intended semantics: Every scope that contains a copy of the shared pointer keeps the object alive by virtue of its "share" in the ownership.
A unique_ptr does not share its pointer. It cannot be copied to another unique_ptr , passed by value to a function, or used in any C++ Standard Library algorithm that requires copies to be made. A unique_ptr can only be moved.
Indeed, once unique, a managed resource can't become shared by an action originating from another thread. The same pattern would be possible with shared_ptr if enable_shared_from_this did not exist. This is why IMHO, unique() has been removed from C++20: misleading.
You should pass around shared pointers exactly as you pass around other objects. If you need to store a copy (of the shared pointer, not the pointed at object), pass by value, and move to its destination.
ServiceA(std::shared_ptr<ServiceB> serviceB)
: _serviceB(std::move(serviceB)) {}
Alternatively, if you don't mind writing two constructors, you can save a tiny bit of performance (one call to the shared pointer's the move constructor) by writing one which takes a const reference and copies it, and one which takes an r-value reference, and moves it.
ServiceA(std::shared_ptr<ServiceB> const& serviceB)
: _serviceB(serviceB) {}
ServiceA(std::shared_ptr<ServiceB> && serviceB)
: _serviceB(std::move(serviceB) {}
Either pass by value (compilers are pretty good at eliding copies) or by const reference - non-const reference as you have makes it look like you intend to modify the parameter.
Also for new code consider using unique_ptr
for parameters and return values where it makes sense, or where the sharing isn't part of the contract. (You can make a shared_ptr
from a unique_ptr
, but not vice versa.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With