21

Is the following a reasonable and efficient approach with regard to creating Stuff and giving Foo ownership of it?

class Foo
{
    explicit Foo(const std::shared_ptr<Stuff>& myStuff)
        : m_myStuff(myStuff)
    {
    }

    ...

private:
    const std::shared_ptr<Stuff> m_myStuff;
}

std::shared_ptr<Stuff> foosStuff(new Stuff());
Foo f(foosStuff);
4
  • 10
    Not really, Foo doesn't take ownership. The point of shared_ptr is to share ownership. Commented Aug 17, 2012 at 8:43
  • Or prefer std::shared_ptr<Stuff> foosStuff(new Stuff()); Commented Aug 17, 2012 at 8:47
  • @juanchopanza You would prefer perhaps to see unique_ptr in this case? Commented Aug 17, 2012 at 8:48
  • 2
    @Baz If you really want to take ownership, then yes. Commented Aug 17, 2012 at 8:48

3 Answers 3

31

Since you are interested in efficiency I'd like to make two points:

shared_ptr<> is one of many standard library types where a move construction is cheaper than a copy construction. Copy constructing shared_ptr is slower since copying requires the reference counters to be incremented atomically whereas moving a shared_ptr doesn't require touching the referenced data or counters at all. From the article "Want Speed? Pass by value!" by Dave Abrahams, one can learn that in certain situations it is actually beneficial to take a function parameter by value. This is one of these cases:

class Foo
{
  explicit Foo(std::shared_ptr<Stuff> myStuff)
  : m_myStuff(move(myStuff))
  {}

  ...

private:
  std::shared_ptr<Stuff> m_myStuff;
};

Now you can write

Foo f(std::make_shared<Stuff>());

where the argument is a temporary and no shared_ptr is ever copied (just moved once or twice).

The use of std::make_shared here has the advantage that only one allocation is done. In your case you allocated the Stuff object by yourself and the shared_ptr constructor had to allocate the reference counters and deleter dynamically as well. make_shared does it all for you with just a single allocation.

Sign up to request clarification or add additional context in comments.

9 Comments

I like that make_shared function
btw: If you change to std::unique_ptr you actually HAVE to pass it by value ;)
@sellibitze What's the benefit of passing the class a shared_ptr in its constructor if you're not going to keep it. Surely in this case it's be better to pass in a unique_ptr and have the class construct its shared_ptr from that?
@jleahy: The "benefit" is that the constructor still accepts a shared_ptr while at the same time there are no copying costs involved. Sure, in this usage example I could have used unique_ptr instead. But this might unnecessarily restrict the interface. If you're going to store a shared_ptr anyways, just accept a shared_ptr as parameter, not a unique_ptr. Whether storing a shared_ptr is a good idea is another question and cannot be simply answered without knowing what Baz intended to do here.
It is important to clarify what version of C++ standard you refer to, otherwise users may mis-interpret the conclusions. So, the answer above is correct as long as you can move-optimise it. It's worth to check Meyers-Alexandrescu-Sutter talk I refer in my answer here stackoverflow.com/a/8741626/151641
|
6

Yes, that's perfectly reasonable. This way the work involved in managing the shared pointer will only need to be done once, rather than twice if you passed it by value. You could also consider using make_shared to avoid a copy construction.

std::shared_ptr<Stuff> foosStuff(std::make_shared<Stuff>());

The only improvement you could make is if Foo is to be the sole owner (ie. you're not going to keep foosStuff around after creating Foo) then you could switch to using a std::unique_ptr or boost::scoped_ptr (which is the pre-C++11 equivalent), which would have less overhead.

6 Comments

OK, I should look into learning more about other smart pointers as I'm only really familiar with shared_ptr.
What is scoped_ptr, it's not part of C++? Maybe you mean std::unique_ptr? And why the custom deleter?
@ChristianRau: I suppose what's mean it boost::scope_ptr. The deleter bit may be an outdated reference to an earlier (stealth) edit?
@KerrekSB I suppose, too, but suppositions don't help anyone reading the answer understand that there is a perfectly suited ownership-taking pointer in the C++ standard library without any need for using boost (which isn't even mentioned). And the question has changed already. Once he notices the question change and inexact information, the downvote on his answer will vanish magically.
@ChristianRau My answer has been made slightly redundant by changes to the question. If it wasn't for the custom deleter I would have also preferred make_shared, like in KerrebSB's answer. I've updated it to prevent confusing people.
|
3

It might be more efficient to have a make_foo helper:

Foo make_foo() { return Foo(std::make_shared<Stuff>()); }

Now you can say auto f = make_foo();. Or at the very least use the make_shared invocation yourself, since the resulting shared_ptr may be more efficient than the one constructed from a new expression. And if Stuff actually takes constructor arguments, a private auxiliary constructor might be suitable:

struct Foo
{
    template <typename ...Args>
    static Foo make(Args &&... args)
    {
        return Foo(direct_construct(), std::forward<Args>(args)...);
    };

private:

    struct direct_construct{};

    template <typeaname ...Args>
    Foo(direct_construct, Args &&... args)
    : m_myStuff(std::make_shared<Stuff>(std::forward<Args>(args)...))  // #1
    {  }
};

You can either wrap Foo::make into the above make_foo, or use it directly:

auto f = Foo::make(true, 'x', Blue);

That said, unless you're really sharing ownership, a std::unique_ptr<Stuff> sounds like more preferable approach: It is both conceptually much simpler and also somewhat more efficient. In that case you'd say m_myStuff(new Stuff(std::forward<Args>(args)...)) in the line marked #1.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.