NoSenseEtAl
NoSenseEtAl

Reputation: 30006

Is there a usage in singlethreaded code where std::shared_ptr is more appropriate than std::unique_ptr

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

Answers (2)

Maxim Egorushkin
Maxim Egorushkin

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

Ernest Friedman-Hill
Ernest Friedman-Hill

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

Related Questions