What C++ Smart Pointer Implementations Are Available

What C++ Smart Pointer Implementations are available?

C++03

std::auto_ptr - Perhaps one of the originals it suffered from first draft syndrome only providing limited garbage collection facilities. The first downside being that it calls delete upon destruction making them unacceptable for holding array allocated objects (new[]). It takes ownership of the pointer so two auto pointers shouldn't contain the same object. Assignment will transfer ownership and reset the rvalue auto pointer to a null pointer. Which leads to perhaps the worst drawback; they can't be used within STL containers due to the aforementioned inability to be copied. The final blow to any use case is they are slated to be deprecated in the next standard of C++.

std::auto_ptr_ref - This is not a smart pointer it's actually a design detail used in conjunction with std::auto_ptr to allow copying and assignment in certain situations. Specifically it can be used to convert a non-const std::auto_ptr to an lvalue using the Colvin-Gibbons trick also known as a move constructor to transfer ownership.

On the contrary perhaps std::auto_ptr wasn't really intended to be used as a general purpose smart pointer for automatic garbage collection. Most of my limited understanding and assumptions are based on Herb Sutter's Effective Use of auto_ptr and I do use it regularly although not always in the most optimized way.


C++11

std::unique_ptr - This is our friend who will be replacing std::auto_ptr it will be quite similar except with the key improvements to correct the weaknesses of std::auto_ptr like working with arrays, lvalue protection via private copy constructor, being usable with STL containers and algorithms, etc. Since it's performance overhead and memory footprint are limited this is an ideal candidate for replacing, or perhaps more aptly described as owning, raw pointers. As the "unique" implies there is only one owner of the pointer just like the previous std::auto_ptr.

std::shared_ptr - I believe this is based off TR1 and boost::shared_ptr but improved to include aliasing and pointer arithmetic as well. In short it wraps a reference counted smart pointer around a dynamically allocated object. As the "shared" implies the pointer can be owned by more than one shared pointer when the last reference of the last shared pointer goes out of scope then the object will be deleted appropriately. These are also thread safe and can handle incomplete types in most cases. std::make_shared can be used to efficiently construct a std::shared_ptr with one heap allocation using the default allocator.

std::weak_ptr - Likewise based off TR1 and boost::weak_ptr. This is a reference to an object owned by a std::shared_ptr and will therefore not prevent the deletion of the object if the std::shared_ptr reference count drops to zero. In order to get access to the raw pointer you'll first need to access the std::shared_ptr by calling lock which will return an empty std::shared_ptr if the owned pointer has expired and been destroyed already. This is primarily useful to avoid indefinite hanging reference counts when using multiple smart pointers.


Boost

boost::shared_ptr - Probably the easiest to use in the most varying scenarios (STL, PIMPL, RAII, etc) this is a shared referenced counted smart pointer. I've heard a few complaints about performance and overhead in some situations but I must have ignored them because I can't remember what the argument was. Apparently it was popular enough to become a pending standard C++ object and no drawbacks over the norm regarding smart pointers come to mind.

boost::weak_ptr - Much like previous description of std::weak_ptr, based on this implementation, this allows a non-owning reference to a boost::shared_ptr. You not surprisingly call lock() to access the "strong" shared pointer and must check to make sure it's valid as it could have already been destroyed. Just make sure not to store the shared pointer returned and let it go out of scope as soon as you're done with it otherwise you're right back to the cyclic reference problem where your reference counts will hang and objects will not be destroyed.

boost::scoped_ptr - This is a simple smart pointer class with little overhead probably designed for a better performing alternative to boost::shared_ptr when usable. It's comparable to std::auto_ptr especially in the fact that it can't be safely used as an element of a STL container or with multiple pointers to the same object.

boost::intrusive_ptr - I've never used this but from my understanding it's designed to be used when creating your own smart pointer compatible classes. You need to implement the reference counting yourself, you'll also need to implement a few methods if you want your class to be generic, furthermore you'd have to implement your own thread safety. On the plus side this probably gives you the most custom way of picking and choosing exactly how much or how little "smartness" you want. intrusive_ptr is typically more efficient than shared_ptr since it allows you to have a single heap allocation per object. (thanks Arvid)

boost::shared_array - This is a boost::shared_ptr for arrays. Basically new [], operator[], and of course delete [] are baked in. This can be used in STL containers and as far as I know does everything boost:shared_ptr does although you can't use boost::weak_ptr with these. You could however alternatively use a boost::shared_ptr<std::vector<>> for similar functionality and to regain the ability to use boost::weak_ptr for references.

boost::scoped_array - This is a boost::scoped_ptr for arrays. As with boost::shared_array all the necessary array goodness is baked in. This one is non-copyable and so can't be used in STL containers. I've found almost anywhere you find yourself wanting to use this you probably could just use std::vector. I've never determined which is actually faster or has less overhead but this scoped array seems far less involved than a STL vector. When you want to keep allocation on the stack consider boost::array instead.


Qt

QPointer - Introduced in Qt 4.0 this is a "weak" smart pointer which only works with QObject and derived classes, which in the Qt framework is almost everything so that's not really a limitation. However there are limitations namely that it doesn't supply a "strong" pointer and although you can check if the underlying object is valid with isNull() you could find your object being destroyed right after you pass that check especially in multi-threaded environments. Qt people consider this deprecated I believe.

QSharedDataPointer - This is a "strong" smart pointer potentially comparable to boost::intrusive_ptr although it has some built in thread safety but it does require you to include reference counting methods (ref and deref) which you can do by subclassing QSharedData. As with much of Qt the objects are best used through ample inheritance and subclassing everything seems to be the intended design.

QExplicitlySharedDataPointer - Very similar to QSharedDataPointer except it doesn't implicitly call detach(). I'd call this version 2.0 of QSharedDataPointer as that slight increase in control as to exactly when to detach after the reference count drops to zero isn't particularly worth a whole new object.

QSharedPointer - Atomic reference counting, thread safe, sharable pointer, custom deletes (array support), sounds like everything a smart pointer should be. This is what I primarily use as a smart pointer in Qt and I find it comparable with boost:shared_ptr although probably significantly more overhead like many objects in Qt.

QWeakPointer - Do you sense a reoccurring pattern? Just as std::weak_ptr and boost::weak_ptr this is used in conjunction with QSharedPointer when you need references between two smart pointers that would otherwise cause your objects to never be deleted.

QScopedPointer - This name should also look familiar and actually was in fact based on boost::scoped_ptr unlike the Qt versions of shared and weak pointers. It functions to provide a single owner smart pointer without the overhead of QSharedPointer which makes it more suitable for compatibility, exception safe code, and all the things you might use std::auto_ptr or boost::scoped_ptr for.

Smart Pointer Implementation in C

Yes, I think it's impossible (or at least, not as useful) because of what @KennyTM says. Smart pointers are possible because of constructors and destructors being automatically called. Otherwise you'll have to call reference() and unreference() yourself. Still useful?

Also, see this previous, very related SO question: Smart pointers/safe memory management for C?

What is the best way to implement smart pointers in C++?

"What is the best way to implement smart pointers in C++"

  1. Don't! Use an existing, well tested smart pointer, such as boost::shared_ptr or std::tr1::shared_ptr (std::unique_ptr and std::shared_ptr with C++ 11)
  2. If you have to, then remember to:

    1. use safe-bool idiom
    2. provide an operator->
    3. provide the strong exception guarantee
    4. document the exception requirements your class makes on the deleter
    5. use copy-modify-swap where possible to implement the strong exception guarantee
    6. document whether you handle multithreading correctly
    7. write extensive unit tests
    8. implement conversion-to-base in such a way that it will delete on the derived pointer type (policied smart pointers / dynamic deleter smart pointers)
    9. support getting access to raw pointer
    10. consider cost/benifit of providing weak pointers to break cycles
    11. provide appropriate casting operators for your smart pointers
    12. make your constructor templated to handle constructing base pointer from derived.

And don't forget anything I may have forgotten in the above incomplete list.

Which smart pointer to use in Stack implementation?

Here is how your stack could be implemented using a std::unique_ptr. Note the use of std::move() to re-assign the std::unique_ptr leaving the original pointing at nothing.

Also, instead of if(topState == NULL) I have used the more idiomatic if(topState). A std::unique_ptr returns true if it points somewhere or false if it does not.

Also standard C++ dictates that we should not begin variable names with a leading _.

#include <string>
#include <memory>
#include <iostream>

struct node
{
std::string gameState;
std::unique_ptr<node> nextGameState;

~node()
{
std::cout << "deleting: " << gameState << '\n';
}
};

class GameStateStack
{
// should not use _ to begin variable names in std C++
std::unique_ptr<node> topState;
int gameStatesCount;

public:
GameStateStack();
void PushGameState(std::string gameStateName);
void PopGameState();
std::string CurrentGameState();
void Destory();
};

GameStateStack::GameStateStack()
: gameStatesCount(0) // initialize here
{
//topState = NULL; // no need to initialize unique_ptr
//gameStatesCount = 0; // not here
}

void GameStateStack::PushGameState(std::string gameStateName)
{
std::unique_ptr<node> newTopState(new node);
newTopState->gameState = gameStateName;

newTopState->nextGameState = std::move(topState);
topState = std::move(newTopState);

gameStatesCount++;
}

void GameStateStack::PopGameState()
{
if(!topState)
std::cout << "Error: no gamestates available to pop";
else
{
topState = std::move(topState->nextGameState);
gameStatesCount--;
}
}

std::string GameStateStack::CurrentGameState()
{
if(topState)
return topState->gameState;
return "error: nothing on stack"; // error
}

void GameStateStack::Destory()
{
// deleting topState will first destroy the pointed to
// node's own unique_ptr<node> nextGameState
// which in turn will first delete its own nextGameState etc...
topState.reset();
}

int main()
{
GameStateStack stack;

std::cout << "\ndestroy test" << '\n';

stack.PushGameState("a");
stack.PushGameState("b");
stack.PushGameState("c");
stack.PushGameState("d");
stack.PushGameState("e");
stack.PushGameState("f");

stack.Destory();

std::cout << "\npush-pop test" << '\n';

stack.PushGameState("a");
stack.PushGameState("b");
stack.PushGameState("c");

std::cout << stack.CurrentGameState() << '\n';

stack.PopGameState();

stack.PushGameState("d");
stack.PushGameState("e");

std::cout << stack.CurrentGameState() << '\n';

stack.PopGameState();

std::cout << stack.CurrentGameState() << '\n';

stack.PopGameState();

std::cout << stack.CurrentGameState() << '\n';

stack.PopGameState();

std::cout << stack.CurrentGameState() << '\n';

stack.PopGameState();

std::cout << stack.CurrentGameState() << '\n';

stack.PopGameState();
}

C++ Smart pointer own implementation

Well some time ago I just made one for studding interest. It's not so "smart" but anyway better then raw pointer.

class CReferenceCount
{
private:
unsigned int m_count;

public:
CReferenceCount() : m_count(0) { }
virtual ~CReferenceCount() { }

void increseRef()
{
++m_count;
}

unsigned int decreseRef()
{
return --m_count;
}
};

class CCustomDeleter
{
public:
template<typename T>
void operator()(const T* ptr) const
{
delete ptr; ptr = NULL;
}

void operator()(const char* ptr) const
{
delete[] ptr; ptr = NULL;
}
};

template <typename T>
class CWrapPtr
{
private:
void makeRefCountObj()
{
try
{
m_rcPtr = new CReferenceCount();
}
catch (std::bad_alloc &err)
{
std::cout<<"-- CWrapPtr : "<<err.what()<<std::endl;
// should do something about failed CWrap object...
}

m_rcPtr->increseRef();
}

public:
T *m_objPtr;
CReferenceCount *m_rcPtr;


CWrapPtr() : m_objPtr(NULL), m_rcPtr(NULL)
{
makeRefCountObj();
}

CWrapPtr(T *obj) : m_objPtr(obj), m_rcPtr(NULL)
{
makeRefCountObj();
}

virtual ~CWrapPtr()
{
if (m_rcPtr && m_rcPtr->decreseRef() == 0)
{
CCustomDeleter dd;
dd(m_objPtr);

delete m_rcPtr; m_rcPtr = NULL;
}
}

CWrapPtr(const CWrapPtr<T> &other) : m_objPtr(other.m_objPtr),
m_rcPtr(other.m_rcPtr)
{
m_rcPtr->increseRef();
}


T& operator*()
{
assert(m_objPtr != NULL);
return *m_objPtr;
}

T* operator->()
{
assert(m_objPtr != NULL);
return m_objPtr;
}

CWrapPtr<T>& operator=(const CWrapPtr<T> &other)
{
if (this != &other)
{

if (m_rcPtr && m_rcPtr->decreseRef() == 0)
{
CCustomDeleter dd;
dd(m_objPtr);

delete m_rcPtr; m_rcPtr = NULL;
}

m_objPtr = other.m_objPtr;
m_rcPtr = other.m_rcPtr;

m_rcPtr->increseRef();
}

return *this;
}
};

And yeah it's just demo..

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.

A simple implementation of Smart Pointer Class

One property of the original implementation is that the delete is performed, in the control block object, with the original pointer type. This is a partial type erasure. No matter how much the smart pointer objects are copied, with somewhat different types, the original control block remains the same, with delete via the original pointer type.

However, since the original code you show is not templated, one must assume that it is an early example, followed later by similar templated code.

Converting a pointer up in a base class hierarchy, as can happen with copying of a smart pointer, means that delete on the new pointer type is only valid if the statically known new type has a virtual destructor.

For example, std::shared_ptr also deletes (guaranteed) via the original pointer type, unless one explicitly supplies a deleter functor that does something else.



Related Topics



Leave a reply



Submit