For Developers‎ > ‎

Smart Pointer Guidelines

What are smart pointers?  

Smart pointers are a specific kind of "scoping object". They are like regular pointers but can automatically deallocate the object they point to when they go out of scope. Since C++ is not a garbage collected language, such functionality is important. The pattern where scoping objects are used to automatically manage the lifetime of heap-allocated objects is called RAII - Resource Acquisition Is Initialization.

Here's some sample use of scoped_ptr, the most common type of smart pointer:

// We put a pointer into a smart pointer at construction time.
scoped_ptr<base::Value> value(base::JSONReader::Read(data));
scoped_ptr<Foo> foo_ptr(new Foo(...));
// ...Or by using reset().
scoped_ptr<Bar> bar_ptr; // Like "Bar* bar_ptr = nullptr;".
bar_ptr.reset(new Bar(...)); // Now |bar_ptr| is non-nullptr and owns the object.

// We can test the smart pointer directly or use get() to see the raw pointer underneath.
if (!value)
  return false;
Foo* raw_ptr = foo_ptr.get();

// We can call through the smart pointer as if it were a pointer.
DictionaryValue* dict = nullptr;
if (!value->GetAsDictionary(&dict))
  return false;

Why do we use them?

Smart pointers ensure we properly destroy an object even if its creation and destruction are widely separated. They make functions simpler and safer by ensuring that no matter how many different exit paths exist, local objects are always cleaned up correctly. They help enforce that exactly one object owns another object at any given time, preventing both leaks and double-frees.

What types of smart pointers exist?

While scoped_ptr is the most common smart pointer, others exist.  For instance, scoped_refptr is used for reference-counted objects, though normally you should avoid these (see below).  Other more esoteric ones exist.  Look in src/base/memory for both the standard and more esoteric pointers. Note that WeakPtr is also a class that works like a pointer, but isn't used for automatically freeing objects like these other cases -- instead it's used when someone else owns the pointer (see below).

When do we use which smart pointer?

  • Owned objects - use scoped_ptr.  Use these for non-reference-counted, heap-allocated objects that you own.
  • Non-owned objects - use raw pointers or WeakPtr.  If other code owns the object, but you need to know when it dies, you can use WeakPtr, which will be automatically set to nullptr on destruction. This way you can test the pointer to see if it's still alive. (You still need to test -- blindly dereferencing a nullptr WeakPtr has the same effect as dereferencing a nullptr pointer.) Note that if you need to take some action immediately before or after the object is destroyed, you'll likely be better-served with some sort of callback or notification instead of a WeakPtr. If you're using multiple threads, WeakPtrs must only be dereferenced on the same thread where they were created (usually by a WeakPtrFactory).
  • Ref-counted objects - use scoped_refptr; but better yet, rethink your design. Reference-counted objects make it difficult to understand ownership and destruction order, especially when multiple threads are involved. There is almost always another way to design your object hierarchy to avoid refcounting. Avoiding refcounting in multithreaded situations is usually easier if you restrict each class to operating on just one thread, and use PostTask() and the like to proxy calls to the correct thread. base::Bind(), WeakPtr, and other tools make it possible to automatically cancel calls to such an object when it dies. Note that too much of our existing code uses refcounting, so just because you see existing code doing it does not mean it's the right solution. (Bonus points if you're able to clean up such cases.)
  • Platform-specific types - use one of the many platform-specific helpers, such as base::win::ScopedHandle, base::win::ScopedComPtr, or base::mac::ScopedCFTypeRef. Note that these may have slightly different usage patterns than scoped_ptr; for example, they might be assigned as outparams via .receive()/.Receive().

What are the calling conventions involving different kinds of pointers?

  • If a function takes a scoped_ptr, that means it takes ownership of the argument. Callers need to use std::move() to indicate that they're passing ownership if the object being passed is not a temporary: // Foo() takes ownership of |bar|. void Foo(scoped_ptr<Bar> bar); ... scoped_ptr<Bar> bar_ptr(new Bar()); Foo(std::move(bar_ptr)); // After this statement, |bar_ptr| is nullptr. Foo(scoped_ptr<Bar>(new Bar())); // No need to use std::move() on temporaries.
  • If a function returns a scoped_ptr, that means the caller takes ownership of the returned object. Usage of std::move() while returning an object is only needed if the return type of the function differs from the type of the local variable. class Base { ... }; class Derived : public Base { ... }; // Foo takes ownership of |base|, and the caller takes ownership of the returned // object. scoped_ptr<Base> Foo(scoped_ptr<Base> base) { if (cond) { return base; // Transfers ownership of |base| back to // the caller. } else if (cond2) { return scoped_ptr<Base>(new Base())); // No std::move() necessary on temporaries. } scoped_ptr<Derived> derived(new Derived()); return std::move(derived); // Note that std::move() is necessary because // type of |derived| is different from the return // type of the function. // Note that on this codepath, |base| gets deleted here. }
  • If a function takes or returns a raw pointer, it may mean no ownership is transferred, or it may not. Much of Chromium was written before scoped_ptr existed, or by people unfamiliar with using it to indicate ownership transfers, and thus takes or returns raw pointers but transfers ownership in the process. Because the compiler can't enforce correct behavior here, this is less safe. Consider cleaning up such code to use smart pointers. Note that RefCounted objects are often returned as bare pointers -- but as previously noted, you should normally avoid those.

What about passing or returning a smart pointer by reference?  

Don't do this. You should especially never see scoped_ptr<>*. This is almost certainly a misunderstanding of what a scoped_ptr does. We also typically don’t return smart pointers by reference.

The one exception is functions can takes a const scoped_refptr& as a parameter which may avoid extra refcount churn. This is also done (though more rarely) for WeakPtr.

I want to use an STL container to hold pointers. Can I use smart pointers?

As of C++11, you can store smart pointers in STL containers. In particular, using or writing your own containers (such as ScopedPtrVector) is not necessary, since you can use std::vector to store scoped_ptrs.
Comments