Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

More Bad Pointers


October, 2005: More Bad Pointers

Pete Becker is a software developer at Dinkumware, Ltd., where he works on standard library implementation and documentation for C, C++, and Java. He is Project Editor for the C++ Standard, and for several years wrote a column for C/C++ Users Journal. He is currently writing a book about TR1. He can be contacted at [email protected].


Last month, we began talking about the new TR1 template class shared_ptr. This month we'll continue that discussion, and next month we'll finish it off with an examination of the two template classes weak_ptr and enable_shared_from_this. As a reminder, Listing 1 provides a synopsis of the relevant parts of the header <memory>, and Listing 2 provides a synopsis of the template class shared_ptr.

Using the Controlled Resource

Once you've created a shared_ptr object, you use the controlled resource in pretty much the way you'd expect. Its member operator-> returns a pointer to the controlled resource, so a shared_ptr<Ty> object acts pretty much like a pointer to Ty. The member function get also returns a pointer to the controlled resource. The member operator* returns a reference to the controlled resource; of course, this reference is only valid if the stored pointer is not null; see Listing 3.

The member functions unique and use_count give you information about the number of shared_ptr objects that own the same resource. The function unique returns true only if the object that it was called with is the sole owner of the resource. The function use_count returns the number of owners. It is intended primarily for debugging.

You can also insert a shared_ptr object into a stream; when you do that, the effect is the same as inserting the pointer returned by get, as shown in Listing 4.

The only mysterious-looking thing in shared_ptr is the operator boolean-type. That obviously illegal declaration is shorthand for an operator that returns an unspecified type that is convertible to bool when used in boolean contexts. The reason for this handwaving is that direct conversions to bool (or to any other built-in type) can cause confusion and ambiguity. So the requirement is that the returned type be convertible to bool, which makes code like 1 + ptr illegal, but preserves the meaning of code like if(ptr). This operator returns false if the shared_ptr object is empty; otherwise, it returns true; see Listing 5.

Pointer Types, shared_ptr Types, and Conversions

If you looked closely at the synopsis of shared_ptr, you may have noticed that its constructors and the reset member functions that take pointers are templates: They can take pointers to types that are different from the type argument of the shared_ptr object. Of course, that freedom isn't absolute. The rule is that the type of the pointer Other* that you pass as an argument has to be convertible to the type Ty* that the shared_ptr<Ty> holds[1]. Internally, the shared_ptr object keeps track of the actual type of the pointer that was passed to it, and when it's time to release the managed resource, it will treat it as an object of type Other, as shown in Listing 6.

Similarly, you can construct a shared_ptr <Ty> object that's a copy of a shared_ptr <Other> object if Other* can be implicitly converted to Ty*. Thus, implicit conversions for shared_ptr types mimic the implicit conversions for pointer types. This mimicry extends to the new style casts, as well. Resist the temptation to write code like this:

// BAD CODE, DON'T DO THIS:
    shared_ptr<base> spb(new derived);
    shared_ptr<derived> spd(static_cast 	<derived*>(spb.get());

The problem is that you now have two independent shared_ptr objects trying to manage the same resource. Your program will do bad things when they both release it. Instead, use the cast functions static_ pointer_cast, dynamic_pointer_cast, and const_pointer_cast that are defined in <memory>, as shown in Listing 7.

Deleters

The shared_ptr destructor deletes the stored pointer. If that's not what you need, you can provide a deleter at the time you create the original shared_ptr object. The deleter must be a callable object that can be called with the argument passed to the constructor. When it's time to release the resource, the deleter is called with the original object pointer, as in Listing 8.

Listing 8 also shows the template function get_deleter. You provide the template function's first type argument explicitly. If the shared_ptr object has a deleter of that type, the function returns a pointer to the deleter; if it doesn't have a deleter or if the deleter is a different type, it returns a null pointer.

Comparing shared_ptr Objects

You can compare two shared_tr<Ty> objects for equality in the usual way. Two such objects a and b are equal if a.get() == b.get(). Listing 9 shows comparisons for equality and inequality. You can also compare two shared_ptr<Ty> objects for relative order with operator<. This lets you use shared_ptr objects as keys in associative containers, as shown in Listing 10.

These comparison operators have a small quirk: Two shared_ptr objects that hold null pointers compare equal, but it's also possible that one is less than the other. That's because a < b and b < a are both required to be true only if a and b own the same resource or if they are both empty. As we saw last month, you create an empty shared_ptr object with the default constructor. Passing a null pointer to the constructor does not create an empty object, so one of the ordering comparisons must be false for a pair of objects constructed from null pointers. To see how this quirk arises in practice, take a look at Listing 11.

Exception Safety

Under the hood, when you create the first shared_ptr object that manages a particular resource, the implementation allocates a control block that holds the original pointer, the reference count [2], and a copy of the deleter, if there is one. If that allocation fails, the implementation releases the resource, just as if the reference count had gone to zero, and rethrows the exception caused by the initial allocation failure. This prevents memory leaks; this is shown in Listing 12.

Next Time

Next time we'll look at the template class weak_ptr, which holds a reference count that doesn't affect releasing of the controlled resource. That may not sound very useful, but there are good reasons for having it. We'll also look at the template class enable_shared_from_this, which enhances a derived type with a member function that returns a shared_ptr object that owns the derived object.

References

  1. [1] In general, this implies that the type Other must be complete, otherwise the compiler would not know that Other* is convertible to Ty*. However, if the type Ty is void (that is, you've created an object of type shared_ptr<void>) the conversion can be done even if Other is not a complete type. That's not allowed, however: TR1 requires that Other be a complete type if you're going to rely on this conversion.
  2. [2] Actually, there are two reference counts: one for the shared_ptr objects and one for the weak_ptr objects; we'll look at the template weak_ptr next time.
CUJ


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.