When to Use Shared_Ptr and When to Use Raw Pointers

When to use shared_ptr and when to use raw pointers?

Your analysis is quite correct, I think. In this situation, I also would return a bare B*, or even a [const] B& if the object is guaranteed to never be null.

Having had some time to peruse smart pointers, I arrived at some guidelines which tell me what to do in many cases:

  • If you return an object whose lifetime is to be managed by the caller, return std::unique_ptr. The caller can assign it to a std::shared_ptr if it wants.
  • Returning std::shared_ptr is actually quite rare, and when it makes sense, it is generally obvious: you indicate to the caller that it will prolong the lifetime of the pointed-to object beyond the lifetime of the object which was originally maintaining the resource. Returning shared pointers from factories is no exception: you must do this eg. when you use std::enable_shared_from_this.
  • You very rarely need std::weak_ptr, except when you want to make sense of the lock method. This has some uses, but they are rare. In your example, if the lifetime of the A object was not deterministic from the caller's point of view, this would have been something to consider.
  • If you return a reference to an existing object whose lifetime the caller cannot control, then return a bare pointer or a reference. By doing so, you tell the caller that an object exists and that she doesn't have to take care of its lifetime. You should return a reference if you don't make use of the nullptr value.

Whether to pass shared pointer or raw pointer to a function

I would suggest the following approach to looking at code something like this:

Sample Image

There are some other options like weak_ptr, but for this it is probably not worth looking at.

So for your example, we can see that ThirdParty_DoStuff does not take ownership, so we won't either, so you can choose between a reference and a pointer depending on if the argument is mandatory or not respectively.

When should I use raw pointers over smart pointers?

No, it's not true. If a function needs a pointer and has nothing to do with ownership, then I strongly believe that a regular pointer should be passed for the following reasons:

  • No ownership, therefore you don't know what kind of a smart pointer to pass
  • If you pass a specific pointer, like shared_ptr, then you won't be able to pass, say, scoped_ptr

The rule would be this - if you know that an entity must take a certain kind of ownership of the object, always use smart pointers - the one that gives you the kind of ownership you need. If there is no notion of ownership, never use smart pointers.

Example1:

void PrintObject(shared_ptr<const Object> po) //bad
{
if(po)
po->Print();
else
log_error();
}

void PrintObject(const Object* po) //good
{
if(po)
po->Print();
else
log_error();
}

Example2:

Object* createObject() //bad
{
return new Object;
}

some_smart_ptr<Object> createObject() //good
{
return some_smart_ptr<Object>(new Object);
}

What is a smart pointer and when should I use one?

UPDATE

This answer is rather old, and so describes what was 'good' at the time, which was smart pointers provided by the Boost library. Since C++11, the standard library has provided sufficient smart pointers types, and so you should favour the use of std::unique_ptr, std::shared_ptr and std::weak_ptr.

There was also std::auto_ptr. It was very much like a scoped pointer, except that it also had the "special" dangerous ability to be copied — which also unexpectedly transfers ownership.

It was deprecated in C++11 and removed in C++17, so you shouldn't use it.

std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership.
// p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.

OLD ANSWER

A smart pointer is a class that wraps a 'raw' (or 'bare') C++ pointer, to manage the lifetime of the object being pointed to. There is no single smart pointer type, but all of them try to abstract a raw pointer in a practical way.

Smart pointers should be preferred over raw pointers. If you feel you need to use pointers (first consider if you really do), you would normally want to use a smart pointer as this can alleviate many of the problems with raw pointers, mainly forgetting to delete the object and leaking memory.

With raw pointers, the programmer has to explicitly destroy the object when it is no longer useful.

// Need to create the object to achieve some goal
MyObject* ptr = new MyObject();
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?

A smart pointer by comparison defines a policy as to when the object is destroyed. You still have to create the object, but you no longer have to worry about destroying it.

SomeSmartPtr<MyObject> ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.

// Destruction of the object happens, depending
// on the policy the smart pointer class uses.

// Destruction would happen even if DoSomething()
// raises an exception

The simplest policy in use involves the scope of the smart pointer wrapper object, such as implemented by boost::scoped_ptr or std::unique_ptr.

void f()
{
{
std::unique_ptr<MyObject> ptr(new MyObject());
ptr->DoSomethingUseful();
} // ptr goes out of scope --
// the MyObject is automatically destroyed.

// ptr->Oops(); // Compile error: "ptr" not defined
// since it is no longer in scope.
}

Note that std::unique_ptr instances cannot be copied. This prevents the pointer from being deleted multiple times (incorrectly). You can, however, pass references to it around to other functions you call.

std::unique_ptrs are useful when you want to tie the lifetime of the object to a particular block of code, or if you embedded it as member data inside another object, the lifetime of that other object. The object exists until the containing block of code is exited, or until the containing object is itself destroyed.

A more complex smart pointer policy involves reference counting the pointer. This does allow the pointer to be copied. When the last "reference" to the object is destroyed, the object is deleted. This policy is implemented by boost::shared_ptr and std::shared_ptr.

void f()
{
typedef std::shared_ptr<MyObject> MyObjectPtr; // nice short alias
MyObjectPtr p1; // Empty

{
MyObjectPtr p2(new MyObject());
// There is now one "reference" to the created object
p1 = p2; // Copy the pointer.
// There are now two references to the object.
} // p2 is destroyed, leaving one reference to the object.
} // p1 is destroyed, leaving a reference count of zero.
// The object is deleted.

Reference counted pointers are very useful when the lifetime of your object is much more complicated, and is not tied directly to a particular section of code or to another object.

There is one drawback to reference counted pointers — the possibility of creating a dangling reference:

// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!

Another possibility is creating circular references:

struct Owner {
std::shared_ptr<Owner> other;
};

std::shared_ptr<Owner> p1 (new Owner());
std::shared_ptr<Owner> p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1

// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!

To work around this problem, both Boost and C++11 have defined a weak_ptr to define a weak (uncounted) reference to a shared_ptr.

New to c++11 features, proper use of shared_ptr?

...it's useful when many instances may be sharing the same object. Correct?

Not exactly. It is useful when many instances may be owning the same object. Sharing is not enough to justify using a std::shared_ptr as there is certainly some overhead to using it.

When you create dynamic resources you need to think about ownership ie. who is responsible for deleting it? The object that should be responsible for deleting the resource should manage the resource using some kind of smart pointer (or a container).

If only one object is responsible for deciding when the resource must be deleted then use a std::unique_ptr. If other objects/functions need to share access to the resource but will never be responsible for deleting it, then pass them a reference or a raw pointer to the resource.

The time to use a std::shared_ptr is when you can not know which of the objects that are sharing the resource will be the one that needs to delete it. In that case each object should hold ownership of the resource by holding a std::shared_ptr.

And even when several objects share ownership through a std::shared_ptr they should still only pass a reference or a raw pointer to objects/functions that do not need ownership rights.

Another problem with passing std::stared_ptr round willy-nilly (where they are not needed) is that they can suffer from the Java memory leak problem. That is when objects never die because some reference to them remains in a forgotten part of the software. They can gradually accumulate and eat away your memory.

Typically you should prefer keeping your resources in a container like a std::vector or a std::map:

std::map<int, Tile> Tiledb;

The container manages the destruction of the Tile so no need for a smart pointer.

If, however, you were using polymorphic Tile objects then you would need to store them using a pointer. For this prefer to use std::unique_ptr:

// When the Tiles are no longer needed after the map is destroyed
std::map<int, std::unique_ptr<Tile>> Tiledb;

If other objects need to keep accessing the Tile objects after the map is destroyed then a std::shared_ptr may be appropriate:

// only when Tiles need to keep living after the map is destroyed.
std::map<int, std::shared_ptr<Tile>> Tiledb;

Should I delete pointer from `new` passed to a function which makes into a `shared_ptr`?

The very idea of a constructor taking a raw pointer is to pass the ownership to std::shared_ptr. So, no, you don't have to delete a raw pointer passed to std::shared_ptr. Doing this will lead to a double deletions, which is UB.

Note that in general passing a raw pointer is dangerous. Consider the following more generalized example:

void addFoo(Foo *foo){
// some code which could throw an exception
auto my_foo = std::shared_ptr<Foo>(foo);
}

If an exception is throw before my_foo is constructed, foo will leak.

If you have no special reason to pass a raw pointer, consider the following alternative:

class Bar {
public:
template<class... Args>
void addFoo(Args... args){
auto my_foo = std::make_shared<Foo>(args...);
}
};

int main() {
auto bar = Bar();
bar.addFoo();
return 0;
}

Here you pass arguments (if you have any) to construct Foo inside addFoo() instead of constructing Foo before invoking addFoo().

Perfect forwarding of args... could be used if it is needed:

    template<class... Args>
void addFoo(Args&&... args){
auto my_foo = std::make_shared<Foo>(std::forward<Args>(args)...);
}


Related Topics



Leave a reply



Submit