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:
- less memory usage (compared to shared_ptr)
- ref-counting is used only when necessary
- no need to write boilerplate code (compared to intrusive_ptr)
- no need for a common base class (as is common when using intrusive_ptr)
The disadvantages I can see are:
- 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 - we cannot up-cast the underlying raw pointer (which is of type
less_intrusive_ref_counter<T> *
) toT *
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. - we must be able to derive from the passed in T, which excludes built-in and (maybe?) POD types, as well as classes marked as
final
. [pointed out by Filip Roséen and Morwenn] - we cannot make an assignment such as the following:
less_intrusive_ptr<Base> p = make_less_intrusive<Derived>()
, becauseless_intrusive_ref_counter<Derived>
does not inherit fromless_intrusive_ref_counter<Base>
.
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.
T
. – Filip Roséen - refp Mar 29 at 1:33final
cannot be derived from either. – Morwenn Apr 2 at 15:59