What Is a Smart Pointer and When Should I Use One

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.

Should i always use smart pointer in C++ 11 [duplicate]

It is hard to imagine situations where you would want to manually delete an object, so in that sense, the answer to your question is "yes, always use smart pointers".

However, raw pointers do have another use case. Smart pointers are all about conferring ownership semantics. A unique_ptr has exclusive ownership of the object it points to, and will destroy the object when the pointer goes out of scope. A shared_ptr implements shared ownership, and the object will be destroyed when the last shared pointer goes out of scope.

Raw pointers are still useful for cases where you want to point to an object without indicating any kind of ownership. You're just pointing to an object you know exists, and that someone else (who owns it) will delete it when the time comes.

Raw pointers are for pointing to objects. Smart pointers are for owning objects.

Is it a good practice to always use smart pointers?

Given the several edits, I have the impression that a comprehensive summary would be useful.

1. When not to

There are two situations where you should not use smart pointers.

The first is the exact same situation in which you should not use a C++ class in fact. IE: DLL boundary if you do not offer the source code to the client. Let say anecdotal.

The second happens much more often: smart manager means ownership. You may use pointers to point at existing resources without managing their lifetime, for example:

void notowner(const std::string& name)
{
Class* pointer(0);
if (name == "cat")
pointer = getCat();
else if (name == "dog")
pointer = getDog();

if (pointer) doSomething(*pointer);
}

This example is constrained. But a pointer is semantically different from a reference in that it may point to an invalid location (the null pointer). In this case, it's perfectly fine not to use a smart pointer in its stead, because you don't want to manage the lifetime of the object.

2. Smart managers

Unless you are writing a smart manager class, if you use the keyword delete you are doing something wrong.

It is a controversial point of view, but after having reviewed so many example of flawed code, I don't take chances any longer. So, if you write new you need a smart manager for the newly allocated memory. And you need it right now.

It does not mean you are less of a programmer! On the contrary, reusing code that has been proved to work instead of reinventing the wheel over and over is a key skill.

Now, the real difficulty start: which smart manager ?

3. Smart pointers

There are various smart pointers out of there, with various characteristics.

Skipping std::auto_ptr which you should generally avoid (its copy semantic is screwed).

  • scoped_ptr: no overhead, cannot be copied or moved.
  • unique_ptr: no overhead, cannot be copied, can be moved.
  • shared_ptr / weak_ptr: some overhead (reference counting), can be copied.

Usually, try to use either scoped_ptr or unique_ptr. If you need several owners try to change the design. If you can't change the design and really need several owners, use a shared_ptr, but beware of references cycles that ought to be broken using a weak_ptr somewhere in the midst.

4. Smart containers

Many smart pointers are not meant to be copied, therefore their use with the STL containers are somewhat compromised.

Instead of resorting to shared_ptr and its overhead, use smart containers from the Boost Pointer Container. They emulate the interface of classic STL containers but store pointers they own.

5. Rolling your own

There are situations when you may wish to roll your own smart manager. Do check that you did not just missed some feature in the libraries your are using beforehand.

Writing a smart manager in the presence of exceptions is quite difficult. You usually cannot assume that memory is available (new may fail) or that Copy Constructors have the no throw guarantee.

It may be acceptable, somewhat, to ignore the std::bad_alloc exception and impose that Copy Constructors of a number of helpers do not fail... after all, that's what boost::shared_ptr does for its deleter D template parameter.

But I would not recommend it, especially for a beginner. It's a tricky issue, and you're not likely to notice the bugs right now.

6. Examples

// For the sake of short code, avoid in real code ;)
using namespace boost;

// Example classes
// Yes, clone returns a raw pointer...
// it puts the burden on the caller as for how to wrap it
// It is to obey the `Cloneable` concept as described in
// the Boost Pointer Container library linked above
struct Cloneable
{
virtual ~Cloneable() {}
virtual Cloneable* clone() const = 0;
};

struct Derived: Cloneable
{
virtual Derived* clone() const { new Derived(*this); }
};

void scoped()
{
scoped_ptr<Cloneable> c(new Derived);
} // memory freed here

// illustration of the moved semantics
unique_ptr<Cloneable> unique()
{
return unique_ptr<Cloneable>(new Derived);
}

void shared()
{
shared_ptr<Cloneable> n1(new Derived);
weak_ptr<Cloneable> w = n1;

{
shared_ptr<Cloneable> n2 = n1; // copy

n1.reset();

assert(n1.get() == 0);
assert(n2.get() != 0);
assert(!w.expired() && w.get() != 0);
} // n2 goes out of scope, the memory is released

assert(w.expired()); // no object any longer
}

void container()
{
ptr_vector<Cloneable> vec;
vec.push_back(new Derived);
vec.push_back(new Derived);

vec.push_back(
vec.front().clone() // Interesting semantic, it is dereferenced!
);
} // when vec goes out of scope, it clears up everything ;)

Are there real use cases for smart pointers?

I am surprised that more than 99% of the given examples are in fact fairly bad examples because in those cases dynamic allocation could be avoided

This is probably because the examples are intended to be super-simple.

So what could be good examples or uses for Smart Pointers in C++?

Never mind "goodness", but consider the following examples of cases where they're relevant:

1. When you can't hold on to the stack

template <typename T>
std::unique_ptr<T[]> print_and_allocate(std::size_t n)
{
std::cout << "Allocating " << n << " elements of size " << sizeof(T) << '\n';
return std::make_unique<T[]>(n);
}

you can't perform the allocation on the stack, because you're returning before your allocated element is used. Also, you can't just return a single constructed or an std::array, because the number of elements to allocate is not known in advance.

2. A need to refer to one of several possible subclasses

class A;
class B : public A { /* ... */ };
class C : public A { /* ... */ };

// ...

auto my_a = std::unique_ptr<A>(condition ? (A*) new B : (A*) new C);
my_a.some_virtual_method();

3. Complex de-allocation logic

A pointer doesn't tell you when/how it needs to be freed/deallocated. You can make a simple assumption ("I need to free the pointer I get from this function"), but this already suggests expressing your assumption in a wrapper class. If the conditions for de-allocation are more complex, and especially if the pointer recipient isn't aware of them, it becomes critical that they be communicated somehow. An std::shared_ptr or std::weak_ptr are two ways to do so; and another is an std::unique_ptr with a custom deleter.

4. When future usability not guaranteed

When the provider of the pointer can't guarantee it will continue to be valid, but expects that it likely will be - you need some kind of wrapper class to reflect that fact and to make sure you don't try to dereference the pointer when you actually can't. This is where std::weak_ptr() comes into play.


More generally: If you needed to allocate memory and hold a pointer before, for some reason, and couldn't make do without it - you most-probably want to switch to using a smart pointer instead. See also:

What is a smart pointer and when should I use one (answer).

Can smart pointers be implicitly used as pointers?

NO! It would be a terrible API. Yes, you could easily implement it within shared_ptr, but just because you could doesn't mean you should.

Why is it such a bad idea? The plain-pointer-based interface of bar doesn't retain an instance of the shared pointer. If bar happens to store the raw pointer somewhere and then exit, there's nothing that guarantees that the pointer it had stored won't become dangling in the future. The only way to guarantee that would be to retain an instance of the shared pointer, not the raw pointer (that's the whole point of shared_ptr!).

It gets worse: the following code is undefined behavior if foo() returns a pointer instance that had only one reference when foo() returned (e.g. if foo is a simple factory of new objects):

AnotherClass *ptr = m.foo().get();
// The shared_ptr instance returned by foo() is destroyed at this point
m.bar(ptr); // undefined behavior: ptr is likely a dangling pointer here

Here are the options; consider those listed earlier first before considering their successors.

  • If bar(AnotherClass *) is an external API, then you need to wrap it in a safe way, i.e. the code that would have called Original::bar should be calling MyWrapped::bar, and the wrapper should do whatever lifetime management is necessary. Suppose that there is startUsing(AnotherClass *) and finishUsing(AnotherClass *), and the code expects the pointer to remain valid between startUsing and finishUsing. Your wrapper would be:

    class WithUsing {
    std::unique_ptr<AnotherClass> owner; /* or shared_ptr if the ownership is shared */
    std::shared_ptr<User> user;
    public:
    WithUsing(std::unique_ptr<AnotherClass> owner, std::Shared_ptr<User> user) :
    owner(std::move(owner)), user(std::move(user)) {
    user.startUsing(owner.get());
    }
    void bar() const {
    user.bar(owner.get());
    }
    ~WithUsing() {
    user.finishUsing(owner.get());
    }
    };

    You would then use WithUsing as a handle to the User object, and any uses would be done through that handle, ensuring the existence of the object.

  • If AnotherClass is copyable and is very cheap to copy (e.g. it consists of a pointer or two), then pass it by value:

    void bar(AnotherClass)
  • If the implementation of bar doesn't need to change the value, it can be defined to take a const-value (the declaration can be without the const as it doesn't matter there):

    void bar(const AnotherClass a) { ... }
  • If bar doesn't store a pointer, then don't pass it a pointer: pass a const reference by default, or a non-const reference if necessary.

    void bar(const AnotherClass &a);
    void bar_modifies(AnotherClass &a);
  • If it makes sense to invoke bar with "no object" (a.k.a. "null"), then:

    1. If passing AnotherClass by value is OK, then use std::optional:

      void bar(std::optional<AnotherClass> a);
    2. Otherwise, if AnotherClass takes ownership, passing unique_ptr works fine since it can be null.

    3. Otherwise, passing shared_ptr works fine since it can be null.

  • If foo() creates a new object (vs. returning an object that exists already), it should be returning unique_ptr anyway, not a shared_ptr. Factory functions should be returning unique pointers: that's idiomatic C++. Doing otherwise is confusing, since returning a shared_ptr is meant to express existing shared ownership.

    std::unique_ptr<AnotherClass> foo();
  • If bar should take ownership of the value, then it should be accepting a unique pointer - that's the idiom for "I'm taking over managing the lifetime of that object":

    void bar(std::unique_ptr<const AnotherClass> a);
    void bar_modifies(std::unique_ptr<AnotherClass> a);
  • If bar should retain shared ownership, then it should be taking shared_ptr, and you will be immediately converting the unique_ptr returned from foo() to a shared one:

    struct MyClass {
    std::unique_ptr<AnotherClass> foo();
    void bar(std::shared_ptr<const AnotherClass> a);
    void bar_modifies(std::shared_ptr<AnotherClass> a);
    };

    void test() {
    MyClass m;
    std::shared_ptr<AnotherClass> p{foo()};
    m.bar(p);
    }

shared_ptr(const Type) and shared_ptr(Type) will share the ownership,
they provide a constant view and a modifiable view of the object, respectively. shared_ptr<Foo> is also convertible to shared_ptr<const Foo> (but not the other way round, you'd use const_pointer_cast for that (with caution). You should always default to accessing objects as constants, and only working with non-constant types when there's an explicit need for it.

If a method doesn't modify something, make it self-document that fact by having it accept a reference/pointer to const something instead.

Why would I want to use a smart pointer in this situation?

For the purposes of this answer I'm redefining setA as:

void setA(A* new_a){a = new_a;}

Consider:

// Using your SomeUtilityClass

int main() {
A a;
SomeUtilityClass u(&a);
// We define a new scope, just because:
{
A b;
u.setA(&b);
}
std::cout << u.getResult() << '\n';
return 0;
}

After the scope is finished, SomeUtilityClass has a dangling pointer and getResult() invokes Undefined Behaviour. Note that this can't be solved with a reference: You would still get a dangling one.

Now consider the version using a smart pointer:

class SomeUtilityClass {
public:
SomeUtilityClass(std::shared_ptr<A>& a) : a{a} {}
double getResult(){return a->get1() + a->get2();}
void setA(std::shared_ptr<A>& new_a){a = new_a;}
private:
std::shared_ptr<A> a;
};

int main() {
std::shared_ptr<A> a{new A};
SomeUtilityClass u{a};
// We define a new scope, just because:
{
std::shared_ptr<A> b{new A};
u.setA(b);
}
std::cout << u.getResult() << '\n';
return 0;
}

Because you have shared ownership, there's no way to get a dangling pointer. The memory pointed to by b will be deleted as usual, but only after u is destroyed(or its pointer is changed).

IMHO, in most cases you should be using smart pointers (Even when at first it doesn't seem to make much sense). It makes maintenance much easier. Use raw pointers only in specific code that actually needs them, and encapsulate/isolate this code as much as possible.

Which kind of pointer do I use when?

Shared ownership:

The shared_ptr and weak_ptr the standard adopted are pretty much the same as their Boost counterparts. Use them when you need to share a resource and don't know which one will be the last to be alive. Use weak_ptr to observe the shared resource without influencing its lifetime, not to break cycles. Cycles with shared_ptr shouldn't normally happen - two resources can't own each other.

Note that Boost additionally offers shared_array, which might be a suitable alternative to shared_ptr<std::vector<T> const>.

Next, Boost offers intrusive_ptr, which are a lightweight solution if your resource offers reference-counted management already and you want to adopt it to the RAII principle. This one was not adopted by the standard.

Unique ownership:

Boost also has a scoped_ptr, which is not copyable and for which you can not specify a deleter. std::unique_ptr is boost::scoped_ptr on steroids and should be your default choice when you need a smart pointer. It allows you to specify a deleter in its template arguments and is movable, unlike boost::scoped_ptr. It is also fully usable in STL containers as long as you don't use operations that need copyable types (obviously).

Note again, that Boost has an array version: scoped_array, which the standard unified by requiring std::unique_ptr<T[]> partial specialization that will delete[] the pointer instead of deleteing it (with the default_deleter). std::unique_ptr<T[]> also offers operator[] instead of operator* and operator->.

Note that std::auto_ptr is still in the standard, but it is deprecated.
§D.10 [depr.auto.ptr]

The class template auto_ptr is deprecated. [ Note: The class template unique_ptr (20.7.1) provides a better solution. —end note ]

No ownership:

Use dumb pointers (raw pointers) or references for non-owning references to resources and when you know that the resource will outlive the referencing object / scope. Prefer references and use raw pointers when you need either nullability or resettability.

If you want a non-owning reference to a resource, but you don't know if the resource will outlive the object that references it, pack the resource in a shared_ptr and use a weak_ptr - you can test if the parent shared_ptr is alive with lock, which will return a shared_ptr that is non-null if the resource still exists. If want to test whether the resource is dead, use expired. The two may sound similar, but are very different in the face of concurrent execution, as expired only guarantees its return value for that single statement. A seemingly innocent test like

if(!wptr.expired())
something_assuming_the_resource_is_still_alive();

is a potential race condition.



Related Topics



Leave a reply



Submit