Reputation: 30006
my understanding of the new memory <memory>
header in C++11 is a bit week, but from what I can tell shared_ptr is refcounted ptr that makes it really really expensive to copy it(esp on for example ARM arch). And unique_ptr is pretty much the very very light wrapper around new/delete. And it is movable, so it is not like are limited by the scope where you created it.
So my question is:
is there a singlethreaded code usage where shared_ptr is prefered to unique_ptr?
Im not interested in answers like: making your singlethreaded code ready for future multithreading. Presume code is and will stay singlthreaded.
Upvotes: 2
Views: 423
Reputation: 136208
This is a very good question.
In a multi-threaded build one basically always pays for atomic reference counter increments/decrements made by shared_ptr<>
, even if objects never get shared between threads.
The other drawback is that the size of shared_ptr<>
is double the size of a plain pointer.
For these two reasons shared_ptr<>
has never been a good choice for performance critical applications.
Multi-threaded applications have a few types of objects that are shared between thread and the majority that are not. Using atomic reference counter increment/decrement is only required for the thread-shared objects and it is silly to pay atomic operation costs for the majority of other objects. Hence it makes good sense to have different (base) types for thread-shared and thread-non-shared objects and manage them using boost::intrusive_ptr<>
. Thread-shared objects have an atomic reference counter while thread-non-shared object get a plain integer counter. E.g.:
#include <atomic>
#include <boost/intrusive_ptr.hpp>
template<class Derived, class Counter>
class RefCounter
{
Counter ref_count_;
friend void intrusive_ptr_add_ref(RefCounter* p) {
++p->ref_count_;
}
friend void intrusive_ptr_release(RefCounter* p) {
if(!--p->ref_count_)
delete static_cast<Derived*>(p);
}
protected:
RefCounter() : ref_count_() {}
};
class NonThreadShared
: public RefCounter<NonThreadShared, unsigned>
{};
class ThreadShared
: public RefCounter<ThreadShared, std::atomic<unsigned> >
{};
int main() {
boost::intrusive_ptr<NonThreadShared> p(new NonThreadShared);
boost::intrusive_ptr<ThreadShared> q(new ThreadShared);
}
Upvotes: 3
Reputation: 81684
Your dwelling on threading here is kind of a red herring; there's a clear contrast between the two, and it has little to nothing to do with threads. If you're using these classes in a single-threaded environment, you may be able to turn off the atomic operations support; for example, with Boost, define the macro BOOST_SP_DISABLE_THREADS
.
You use shared_ptr<>
when you're not quite sure what the lifetime of an object will be, and want "the last guy in the room to shut off the lights" -- i.e., you don't want the object to be deleted until no client is using it any longer. You use unique_ptr<>
when you know exactly who is going to delete the object -- i.e., when the lifetime of the pointed-to object is precisely delimitated by a scope.
It is true that copying a shared_ptr<>
is not free, but it's far from "really, really expensive." You pay a little bit for the reference counting overhead, but that's the whole point of using it: you need to keep track of the clients of the object; there's a little cost involved, but you get the benefit of not leaking the object.
Upvotes: 4