33

What is the best practice when returning a smart pointer, for example a boost::shared_ptr? Should I by standard return the smart pointer, or the underlying raw pointer? I come from C# so I tend to always return smart pointers, because it feels right. Like this (skipping const-correctness for shorter code):

class X
{
public:
    boost::shared_ptr<Y> getInternal() {return m_internal;}

private:
    boost::shared_ptr<Y> m_internal;
}

However I've seen some experienced coders returning the raw pointer, and putting the raw pointers in vectors. What is the right way to do it?

Rolle
  • 2,900
  • 5
  • 36
  • 40

9 Answers9

24

There is no "right" way. It really depends on the context.

You can internally handle memory with a smart pointer and externally give references or raw pointers. After all, the user of your interface doesn't need to know how you manage memory internally. In a synchronous context this is safe and efficient. In an asynchronous context, there are many pitfalls.

If you're unsure about what to do you can safely return smart pointers to your caller. The object will be deallocated when the references count reaches zero. Just make sure that you don't have a class that keeps smart pointers of objects for ever thus preventing the deallocation when needed.

As a last note, in C++ don't overuse dynamically allocated objects. There are many cases where you don't need a pointer and can work on references and const references. That's safer and reduces the pressure on the memory allocator.

Edouard A.
  • 6,100
  • 26
  • 31
  • You can safely return a `shared_ptr` (or `weak_ptr` and doubtless a few other implementations), but not a `unique_ptr`. (Well, you can return `unique_ptr&&`.) – Mike C Mar 06 '14 at 00:00
  • 2
    @MikeC: It is perfectly safe to return a `unique_ptr`. And in fact, almost always unsafe to return a `unique_ptr&&`. – Benjamin Lindley Nov 08 '15 at 17:12
11

It depends on what the meaning of the pointer is.

When returning a shared_pointer, you are syntactically saying "You will share ownership of this object", such that, if the the original container object dies before you release your pointer, that object will still exist.

Returning a raw pointer says: "You know about this object, but don't own it". It's a way of passing control, but not keeping the lifetime tied to the original owner.

(in some older c-programs, it means "It's now your problem to delete me", but I'd heavily recommend avoiding this one)

Typically, defaulting to shared saves me a lot of hassle, but it depends on your design.

Todd Gardner
  • 13,313
  • 39
  • 51
8

I follow the following guidelines for passing pointers arguments to functions and returning pointers:

boost::shared_ptr

API and client are sharing ownership of this object. However you have to be careful to avoid circular references with shared_ptr, if the objects represent some kind of graph. I try to limit my use of shared_ptr for this reason.

boost::weak_ptr / raw pointer

API owns this object, you are allowed share it while it is valid. If there is a chance the client will live longer than the api I use a weak_ptr.

std::auto_ptr

API is creating an object but the client owns the object. This ensures that the returning code is exception safe, and clearly states that ownership is being transferred.

boost::scoped_ptr

For pointers to objects stored on the stack or as class member variables. I try to use scoped_ptr first.

Like all guidelines there will be times when the rules conflict or have to be bent, then I try to use intelligence.

David Miani
  • 14,518
  • 2
  • 47
  • 66
iain
  • 10,798
  • 3
  • 37
  • 41
7

I typically return "owning"/"unique" smart pointers from factories or similar to make it clear who is responsible for cleaning up.

This example https://ideone.com/qJnzva shows how to return a std::unique_ptr that will be deleted when the scope of the variable that the caller assigns the value to goes out of scope.

While it's true that the smart pointer deletes its own pointer, the lifetime of the variable holding the smart pointer is 100% controlled by the caller, so the caller decides when the pointer is deleted. However, since it's a "unique" and "owning" smart pointer, no other client can control the lifetime.

Johann Gerell
  • 24,991
  • 10
  • 72
  • 122
  • 1
    Eh? Why downvoting that without explanation? Someone with too little large-scale enterprise app code under his belt? – Johann Gerell Jun 11 '09 at 23:15
  • Why downvote? First, I think your use of "unique" was not necessarily intended to mean `unique_ptr` but such a beast exists and your answer confuses. If you did mean `unique_ptr`, then how do you "return" it? (As I noted in another comment above, `unique_ptr&&` will work.) Second, the responsibility for cleaning up lies with the smart pointer itself, and by returning a smart pointer you're absolving the client of any need to worry about cleaning up. – Mike C Mar 06 '14 at 00:07
  • @MikeC: *I think your use of "unique" was not necessarily intended to mean `unique_ptr`* It wasn't, hence the usage of "unique". I meant anything behaving like `std::unique_ptr`. *but such a beast exists and your answer confuses.* It's not a beast and if it was confusing, I'll try to reformulate myself. – Johann Gerell May 30 '14 at 11:18
  • @MikeC: *If you did mean unique_ptr, then how do you "return" it?* As you would with anything else smart pointer-ish: https://ideone.com/qJnzva *(As I noted in another comment above, unique_ptr&& will work.)* As shown in https://ideone.com/qJnzva it's not needed. – Johann Gerell May 30 '14 at 11:22
  • @MikeC: *Second, the responsibility for cleaning up lies with the smart pointer* While that is true, the cleanup is tied to the caller's scope, which is what I meant with "make it clear who is responsible for cleaning up." - the caller controls the cleanup by virtue of the lifetime of the variable getting the returned smart pointer. *by returning a smart pointer you're absolving the client of any need to worry about cleaning up.* Again, the cleanup is still 100% controlled by the caller, since the caller controls the lifetime/scope of the assigned-to variable. – Johann Gerell May 30 '14 at 11:22
  • @MikeC: Added some text to my answer to make it clearer what I meant. – Johann Gerell May 30 '14 at 11:27
4

I would never return a raw pointer, instead I would return a weak_ptr to tell the user of the pointer that he doesn't have the control over the resource.

If you return a weak_ptr its very unlikely that there will be dangling pointers in the application.

If there is a performance problem I would return a reference to the object and a hasValidXObject method.

TimW
  • 8,351
  • 1
  • 29
  • 33
3

In my opinion, in C++, you should always have to justify the use of an unguarded pointer.

There could be many valid reasons: a need for very high performance, for very low memory usage, for dealing with legacy libraries, because of some issue with the underlying data structure the pointer is storing. But [dynamically allocated] pointers are somewhat 'evil', in that you have to deallocate the memory at every possible execution path and you will almost certainly forget one.

Oliver N.
  • 2,496
  • 19
  • 20
  • 1
    The goto statement may be used (in non-spagetti mode) to ensure that every exit point goes through a block of code... ergo: you can roll your own throw-try-catch-finally... we did... it works brilliantly, and without the massive overheads of Java/C#'s exception handling. – corlettk Jun 10 '09 at 13:13
0

depends on your goals.

blindly returning smart ptr to internal data might not be a good idea (which is very sensitive to the task you're trying to solve) - you might be better off just offering some doX() and doY() that use the pointer internally instead.

on the other hand, if returning the smart ptr, you should also consider that you'll create no mutual circular references when objects end up unable to destroy each other (weak_ptr might be a better option in that case).

otherwise, like already mentioned above, performance/legacy code/lifetime considerations should all be taken into account.

Benoît
  • 16,798
  • 8
  • 46
  • 66
deemok
  • 2,735
  • 19
  • 11
0

I wouldn't put raw pointers in vectors.

In case they use auto_ptr or boost::scoped_ptr, they can't use (or return) anything but raw pointers. That could explain their way of coding, i guess.

Benoît
  • 16,798
  • 8
  • 46
  • 66
  • I don't understand what you mean by "In case they use auto_ptr or boost::scoped_ptr, they can't use (or return) anything but raw pointers." Do you mean that a function can't return an auto_ptr, or simply that sharing can't be expressed with auto_ptr? – Éric Malenfant Jun 10 '09 at 14:22
  • I simply mean that auto_ptr (or scoped_ptr) can't be used in a container. – Benoît Jun 10 '09 at 22:24
-2

const boost::shared_ptr &getInternal() {return m_internal;}

This avoids a copy.

Sometimes you'll like to return a reference, for example:

  • Y &operator*() { return *m_internal; }
  • const Y &operator*() const { return *m_internal; }

This is good too only if the reference will be used and discarded inmediately. The same goes for a raw pointer. Returning a weak_ptr is also an option.

The 4 are good depending on the goals. This question requires a more extensive discussion.

chila
  • 2,372
  • 1
  • 16
  • 33
  • No, never return a reference to a `shared_ptr`. Copy, or `weak_ptr`. Copying the `shared_ptr` is (reasonably) cheap. – Mike C Mar 06 '14 at 00:01
  • Not a reference, but a const reference. My answer is the correct one. It's safe and performs better than anything. – chila Mar 27 '14 at 20:21