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.
  3. 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]
  4. we cannot make an assignment such as the following: less_intrusive_ptr<Base> p = make_less_intrusive<Derived>(), because less_intrusive_ref_counter<Derived> does not inherit from less_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.


migrated from stackoverflow.com Apr 2 '15 at 14:50

This question came from our site for professional and enthusiast programmers.

  • 2
    \$\begingroup\$ another (major) disadvantage; you must be able to derive from the passed in T. \$\endgroup\$ – Filip Roséen - refp Mar 29 '15 at 1:33
  • \$\begingroup\$ I guess you're right. By the way, which types cannot be derived from? I'm thinking only of built-in or POD datatypes, is that correct? \$\endgroup\$ – Diego Sogari Mar 29 '15 at 1:52
  • 1
    \$\begingroup\$ Types marked as final cannot be derived from either. \$\endgroup\$ – Morwenn Apr 2 '15 at 15:59

To answer my own question, in light of the disadvantages that were gathered, I guess that the usage of the proposed smart pointer would be severely limited. Hence, I would rather rely on the flexibility of the standard smart pointers, especially through the use of std::make_shared.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.