Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it a good practice to always use smart pointers?

Tags:

c++

People also ask

Should I use smart pointers everywhere?

You (or your library) should use a smart pointer to keep track of the entire allocated string, but you might use a raw pointer to step through each character to parse or modify. You wouldn't use a smart pointer because the object it is pointing at is not owned by the pointer.

Are smart pointers worth it?

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.

Why are smart pointers better than raw pointers?

Smart pointers are class objects that behave like raw pointers but manage objects that are new and when or whether to delete them— smart pointers automatically delete the managed object at the appropriate time.


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 ;)

Smart pointers do perform explicit memory management, and if you don't understand how they are doing it, you are in for a world of trouble when programming with C++. And remember that memory isn't the only resource that they manage.

But to answer your question you should prefer smart-pointers as a first approximation to a solution, but possibly be prepared to ditch them when necessary. You should never use pointers (or any sort) or dynamic allocation when it can be avoided. For example:

string * s1 = new string( "foo" );      // bad
string s2( "bar" );    // good

Edit: To answer your suplementary question "Can I always use smart pointers in place of raw pointers??? Then, no you can't. If (for example) you need to implement your own version of operator new, you would have to make it return a pointer, not a smart pointer.


Usually you shouldn't use pointers (smart or otherwise) if you don't need them. Better make local variables, class members, vector elements and similar items normal objects instead of pointers to objects. (Since you come from Java you're probably tempted allocate everything with new, which is not recommended.)

This approach ("RAII") saves you from worrying about pointers most of the time.

When you have to use pointers it depends on the situation and why exactly you need pointers, but usually smart pointers can be used. It might not be always (in bold) be the best option, but this depends on the specific situation.


A good time not to use smart pointers, is at the interface boundary of a DLL. You don't know whether other executables will be built with the same compiler/libraries. Your system's DLL calling convention won't specify what standard or TR1 classes look like, smart pointers included.

Within an executable or library, if you want to represent ownership of the pointee, then smart pointers are on average the best way to do it. So it's fine to want to always use them in preference to raw. Whether you actually can always use them is another matter.

For a concrete example when not to - suppose you are writing a representation of a generic graph, with vertices represented by objects and edges represented by pointers between the objects. The usual smart pointers will not help you: graphs can be cyclic, and no particular node can be held responsible for the memory management of other nodes, so shared and weak pointers are insufficient. You might for example put everything in a vector and use indices instead of pointers, or put everything in a deque and use raw pointers. You could use shared_ptr if you wanted, but it won't add anything except overhead. Or you could look for mark-sweep GC.

A more marginal case: I prefer to see functions take a parameter by pointer or reference, and promise not to retain a pointer or reference to it, rather than take a shared_ptr and leave you wondering whether maybe they retain a reference after they return, maybe if you modify the referand ever again you'll break something, etc. Not retaining references is something that often isn't documented explicitly, it just goes without saying. Maybe it shouldn't, but it does. Smart pointers imply something about ownership, and falsely implying that can be confusing. So if your function takes a shared_ptr, be sure to document whether it can retain a reference or not.