Is It a Good Practice to Always Use Smart Pointers

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

Should i always use smart pointer in C++ 11

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.

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.

Should I use a smart pointer?

Always use a smart pointer wherever you own resources (memory, files etc). Owning them manually is extremely error prone and violates many good practices, like DRY.

Which one to use depends on what ownership semantics you need. unique_ptr is best for single ownership, and shared_ptr shared ownership.

As children do not own their parents, a raw parent pointer is fine. However, if the parents own their children, unique_ptr works best here.

It's also notable that what on earth, a linked list of pointers? That makes no sense. Why not a linked list of values?

Is smart pointer a good practice of RAII?

From cppreference:

Resource Acquisition Is Initialization or RAII, is a C++ programming technique which binds the life cycle of a resource that must be acquired before use (allocated heap memory, thread of execution, open socket, open file, locked mutex, disk space, database connection—anything that exists in limited supply) to the lifetime of an object.

std::shared_ptr is definitely RAII as it aquires a resource and binds its lifetime to its own, thus taking over the responsibility of releasing/destructing the resource. This is the core principle of RAII.

The term RRID (Resource Release Is Destruction) is rarely seen and its meaning seems to be somewhat ambiguous. Mostly it is used with the same meaning as RAII.

IMHO many discussions revolving around variants of RAII deem from interpreting the meaning of the term too exactly. RAII is meant to represent a concept of object life-time management.

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.

How efficient smart pointers are?

The rule of thumb is:

  1. If you can, use a stack based object directly or by a reference instead of using pointers.
  2. Else, if you don't have to deal with shared ownership (usually you don't) use unique_ptr - it is not only faster, but also safer (no circular references).
  3. Else, if you do have shared ownership, use shared_ptr

Raw pointers are OK in certain circumstances when they don't carry ownership - for instance as an input argument to a function:

void draw (const shape* sh) {
sh->draw();
}

...
std::unique_ptr<shape> ptr(new triangle);
draw(ptr.get());


Related Topics



Leave a reply



Submit