The Missing C++ Smart Pointer

Posted

In C++ RAII is the core technique for managing resources. In fact I would argue that it is the only sane way to manage resources beyond simple memory. However in the past the major downfall was dynamically allocated objects. If you allocated an object using new you had to make sure you deleted it at the proper time.

Luckily smart pointers were created. These are tiny wrappers around pointers that automatically free them when they go out of scope. You can also put them in standard containers and they will be freed when they are removed (unless you take control of them).

The first iteration was std::auto_ptr but it had some downsides. So in C++11 we got std::unique_ptr as well as reference counting pointers via std::shared_ptr and std::weak_ptr.

These should cover most of your needs but there is one use case that I feel was left out. If you have a polymorphic object you can't pass it around by value because object slicing will occur. Instead, in true RAII fashion you pass around a std::unique_ptr to it instead. This way you still get the benefits of RAII while passing around a pointer.

However consider the following situation.

unique_ptr<int> i(new int(5));
auto i2 = i; // error: call to deleted constructor.

You can't copy a std::unique_ptr because it is unique. However what you might want to do is create a new std::unique_ptr that contains a copy of the data pointed to by the original.

This may seem like a silly problem, as you could copy yourself before assigning, however often the copy happens outside of your control. For example you can't copy a std::vector<std::unique_ptr<T>>. Sometimes you want your pointers managed, but you don't actually care how many of each object you have.

So I created a class proxy_ptr (names are hard) that is very similar to std::unique_ptr except in places where std::unique_ptr would raise a compiler error because a copy was attempted, proxy_ptr instead calls the #clone() method of the source object and stores the new object in the destination pointer.

A quick note is that if you are doing polymorphism (which is one of the main reasons to use proxy_ptr) your #clone() method must be virtual or you will always get copies of the base class.

#include <memory>

using namespace std;

template <class T, class Deleter = std::default_delete<T> >
class proxy_ptr : public unique_ptr<T,Deleter> {
	typedef proxy_ptr <T,Deleter> C;
	typedef unique_ptr<T,Deleter> B;
public:
	/// Construct from plain pointer.
	proxy_ptr(T *p=NULL) noexcept: B(p) {}
	
	/// Copy constructors.
	proxy_ptr(const B &p): B(p->clone()) {}
	proxy_ptr(const C &p): B(p->clone()) {}
	
	/// Converting Copy constructors.
	template <class T2, class D2>
	proxy_ptr(const proxy_ptr<T2,D2> &p): B(p->clone()) {}
	
	/// Move constructor.
	template <class T2, class D2>
	proxy_ptr(proxy_ptr<T2,D2> &&p) noexcept: B(p.release()) {}
};

Now you can copy your pointers to copy your objects, and they are all automatically freed.

proxy_ptr<MyClass> a(new MyClass()), b, c;
b = a; // Allocates a new object and assigns it to b.
b = c; // Frees the object b held, and empties b.
c = b = a; // Allocates two new objects.
b = c; // Frees b's object, and gives it a new copy of c's.
// a, b and c free their objects when they go out of scope.

The code is just a quick implementation. It relies on the #clone() method because there is no way to clone any object in C++. It would also be useful to add methods for converting to and from the other smart pointers. If you improve upon it please send me the changes.