Channels ▼
RSS

C/C++

A Base Class for Intrusively Reference-Counted Objects in C++


Cv-qualifier

A smart pointer should operate on both constant and volatile objects. But the compiler complains about a line as innocent as:

boost::intrusive_ptr<const foo::foo> p1(new foo::foo());

The problem is that the two free functions expect a pointer to a non-const ref_counter object, because the counter has to be manipulated. The solution is straightforward:

// ref_counter.h: Adding support for constant objects
class ref_counter
{
// …
	friend void intrusive_ptr_add_ref(const ref_counter* p);
	friend void intrusive_ptr_release(const ref_counter* p);
private:
	mutable unsigned long counter_;
};
The two free functions accept a pointer to a const ref_counter object while the counter is mutable, so it can yet be manipulated. I leave the support for the volatile qualifier as an exercise.

Polymorphism

The ref_counter class has a virtual destructor. A virtual destructor forces objects to carry a pointer to a virtual method table along with them. If intrusive_ptr_release did not polymorphically delete the object using a pointer to the base class, but using a pointer to the derived class, the virtual destructor could be abandoned. The intrusive_ptr class template calls the two free functions with a pointer, whose type is parameterized. This type is the derived class, not the ref_counter base class. Perfect. This means that intrusive_ptr_release can take a pointer to the derived class, but therefore, it must know the type of the derived class. Coplien's "Curiously Recurring Template Pattern" (CRTP) [C++Templates 16.3] provides the solution to this dilemma: A class, X, is derived from a class template instantiation, base, and uses itself as the template argument, class X: base<X>. Here is the definition of the class template announcing the type of the derived class using CRTP, the free functions taking pointers to the derived class and therefore obviating deletion using a base class pointer:

// ref_counter.h: disposal using a pointer to the derived class
template <class Derived>
class ref_counter
{
	friend void intrusive_ptr_add_ref(const Derived* p);
	friend void intrusive_ptr_release(const Derived* p);
// …
protected:
	~ref_counter() {}; 	// non-virtual
//~ref_counter() = default; // in C++0x
};

The class definition of foo has to pass its type to the base class template ref_counter:

class foo: public intrusive::ref_counter<foo> {};

Meyers uses CRTP to provide a static counter for different derived types [Counting]. Here, I use CRTP in combination with the "friend name injection" (a component of the Barton-Nackman Trick) [C++Templates 9.2.2 and 11.7] to inject the two free non-template functions into namespace scope, accepting pointers to the derived class instead of pointers to the base class. The ref_counter class template does not need a virtual destructor anymore. Of course the derived class may need one.

Enabling Private Inheritance

A derived foo class has no conceptual relationship to ref_counter, there is no reason for ref_counter to add to foo's interface. Instead, foo uses the generic reference-counting implementation of ref_counter, it is implemented in terms of ref_counter. This is expressed with private inheritance, while public inheritance supplies an interface, not an implementation [EffectiveC++ item 42].

A client could gather a pointer to the base class with an implicit upcast. Is that sensible? Or is it even a violation of the Liskov Substitution Principle [LSP]? Clearly, foo should not be used as a ref_counter, foo IS-NOT-A ref_counter. So, it should not be possible to provide a foo where a ref_counter is expected. Class design should prevent the user from misuse. Prohibiting the implicit conversion from the derived class, foo, to the base class, ref_counter, can be done by private inheritance:

class foo: intrusive::ref_counter<foo> {};

Now the compiler complains of an inaccessible conversion from a pointer to the derived class to a pointer to the base class. The problem is that the two free functions have to access the counter in the base class through a pointer to the derived class. An implicit conversion and a static_cast from a pointer to the derived class to a pointer to the inaccessible base class both are ill-formed. Obviously, dynamic_cast (downcast) or const_cast (cv-qualifier conversion) won't do the trick. A reinterpret_cast is not only dangerous and implementation dependent, but it will fail in multiple inheritance. This leaves us with the dangerous explicit C-style cast. It will work even if the base class type is not accessible [C++03 §5.4-7 and §11.2-3], but the class type must be complete — otherwise, the explicit C-style cast may be interpreted as a reinterpret_cast instead of a static_cast. By the way, the delete expression also expects a complete class type if the class has a non-trivial destructor [C++03 §5.3.5-5]. ref_counter is complete, as within the class member specification, the class is regarded as complete within function bodies [C++03 §9.2-2]. Declarations inside the base class template are instantiated when the derived class is declared, but their bodies are instantiated after the full declaration or instantiation of the derived class, so Derived is complete [TMP 9.8]. Therefore, inside the friend function bodies, both object-types are completely defined. The C-style cast and the delete expression are safe and well defined:

//  ref_counter.h: enabling private inheritance
template <class Derived>
class ref_counter
{
	friend void intrusive_ptr_add_ref(const Derived* p)
	{
		BOOST_ASSERT(p);
		++((const ref_counter*) p)->counter_;
	}
	friend void intrusive_ptr_release(const Derived* p)
	{
		BOOST_ASSERT(p);
		if (--((const ref_counter*) p)->counter_ == 0)
			delete p;
	}
//…
};

Private inheritance is enabled and so is the foo IS-NOT-A ref_counter property.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video