samedi 28 mars 2015

Implementation of a new kind of smart pointer in C++

I'm tempted to write a new kind of smart pointer which, I believe, is less intrusive than boost::intrusive_ptr and has the same memory footprint.


The code for the smart pointer, which I called less_intrusive_ptr, is shown below (the boost version used is 1.57.0):



#include <utility>
#include <boost/intrusive_ptr.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>

template <class T>
class less_intrusive_ref_counter :
public boost::intrusive_ref_counter<less_intrusive_ref_counter<T>>, public T
{
using T::T;
};

template <class T>
class less_intrusive_ptr :
public boost::intrusive_ptr<less_intrusive_ref_counter<T>>
{
using boost::intrusive_ptr<less_intrusive_ref_counter<T>>::intrusive_ptr;
};

template <class T, class... Args>
less_intrusive_ptr<T> make_less_intrusive(Args&&... args)
{
return less_intrusive_ptr<T>(
new less_intrusive_ref_counter<T>(std::forward<Args>(args)...));
}


The code used to test it is shown below:



#include <iostream>
#include <boost/smart_ptr/shared_ptr.hpp>
#include "less_intrusive_ptr.h"

class MyClass1 {};
class MyClass2 : public boost::intrusive_ref_counter<MyClass2> {};
class MyClass3 {};

int main()
{
boost::shared_ptr<MyClass1> p1(new MyClass1);
boost::intrusive_ptr<MyClass2> p2(new MyClass2);
less_intrusive_ptr<MyClass3> p3(new less_intrusive_ref_counter<MyClass3>);
// or, more friendly syntax:
//auto p3 = make_less_intrusive<MyClass3>();

std::cout << sizeof(p1) << std::endl; // output: 8
std::cout << sizeof(p2) << std::endl; // output: 4
std::cout << sizeof(p3) << std::endl; // output: 4
return 0;
}


The advantages I can see are:



  1. less memory usage (compared to shared_ptr)

  2. ref-counting is used only when necessary

  3. no need to write boilerplate code (compared to intrusive_ptr)

  4. no need for a common base class (as is common when using intrusive_ptr)


The disadvantages I can see are:



  1. we cannot use an existing raw pointer and add ref-counting behavior to it, we must always create an object by means of less_intrusive_ref_counter when we need such behavior

  2. we cannot up-cast the underlying raw pointer (which is of type less_intrusive_ref_counter<T> *) to T * and then try to delete it, unless T's destructor is declared virtual. If it's not, and we try to delete the object trough T's pointer, we get undefined behavior.


My question is: can you spot other disadvantages to this approach? In fact, do you think this design is flawed in some way? I really am not an experienced C++ programmer.


Aucun commentaire:

Enregistrer un commentaire