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

Pointing in the Right Direction

By Jim Hyslop and Herb Sutter

, November 01, 2003


Pointing in the Right Direction

It was one of those marathon debugging sessions. Nothing particularly difficult, just repetitive and time consuming. You know the kind I mean: tracking down memory leaks and program crashes. I tracked down the majority of the leaks to a particular class. Simplified somewhat, it looked something like this:

class U;
class T
{
public:
  void f( U * );
  void g( U * );
};

Problem was, the f() function was crashing:

#include "u.h"
void T::f( U * u )
{
  ... some processing with u
  delete u; // crashed here
}

I followed the stack trace back, and discovered that the program was trying to delete an automatic variable:

{//... some block scope
  T t;
  U u1;
  t.f(&u1);
  U u2;
  t.g( &u2 );
}

"Well, that'll do it," I mumbled. I modified the function to use dynamically allocated objects, and modified the call to g() at the same time:

{//... some block scope
  T t;
  U *u = new U;
  t.f( u );
  u = new U;
  t.g( u );

I compiled and ran the program, and that fixed the crash. Satisfied, I exited the program — and was promptly smacked in the face with fresh memory leaks. "What the...," I muttered.

"Keep it down over there, pardner," my friend and cubicle neighbor Wendy said. Well, not in so many words — the wadded-up paper ball that landed on my keyboard was her way of telling me that I was being a little louder than she liked.

After acknowledging her request (by returning the paper in the same manner it was delivered), I turned my attention to the code once again. I had a closer look at g(). Sure enough, it did not delete the object it was provided:

void T::g( U * u )
{
  ... some processing with u
  ... but not a delete in sight.
}

Grumbling — quietly — I fixed up the calling code. Again.

{//... some block scope
  T t;
  U *u = new U;
  t.f( u );
  U u1;
  t.g( &u1 );

This time, when I ran it, I was back to the leaks I had when I started the debugging session.

Ever have the sudden feeling that somebody's watching you? I did. On a hunch, without turning, I said in my best mystic voice, "Yes, apprentice?" The squeak behind me told me I had not been wrong. I turned, and faced our intern, Kerry.

"I, um," Kerry stammered, "That is, the, uh, boss..."

"The Guru?" I interrupted. Kerry nodded. He had difficulty allowing himself to call her "the Guru," even though that's what everyone called her. Except Bob, whose nicknames for the Guru were not flattering, and do not bear repeating here. I don't know if anyone in the department knew the Guru's real name.

"Yeah, uh, her," he said. "She came by and told me you wanted to see me."

I had no idea what he was talking about — I hadn't asked to see him. I stared at him while I tried to figure out why the Guru would have said that. Kerry started shifting from foot to foot. Suddenly, the penny dropped — Kerry had written the class I was working on.

"Very good, apprentice, pull up a chair," I said, as I turned to the keyboard.

"The problem is in this class you wrote," I pointed at the class T on my monitor. "Specifically, it lies in these two functions," I pointed at f() and g(). "Raw pointers are a problem, because they don't tell you anything about who owns the memory. In this first function," I pointed to f(), "You want a pointer to dynamically allocated memory, and you take ownership of that memory. In the second function, though," I pointed to g(), "You don't take ownership of the memory pointed to."

"But this is all documented in the design document," Kerry protested. I waved him off.

"You are presuming the design document will get read. In many cases, it does not — I don't have time to read the fine print in every single document. The code itself should be self documenting. Can you think of any way to tell other programmers your intent?" I paused. It was a curious sensation, watching someone else squirm mentally, the way the Guru...

Snap! A large tome snapped closed behind us. Both of us jumped.

"My apprentice, do not presume that others will not read the blessed design documents," the Guru remonstrated. "You are, however, correct in saying that your writings should be self documenting. Young apprentice," she said to Kerry, "The question stands — how can you evangelize your meanings to other blessed scribes?"

Kerry opened his mouth a couple of times, but no sound came out. The Guru turned to me. "Apprentice?" Suddenly caught in the spotlight, I realized I hadn't thought it through either. I mumbled a few things — I wasn't feeling very smart and wasn't getting to the point. Smart...point..."Smart pointers!" I blurted out.

"Exactly right, my apprentice," the Guru smiled. "Smart pointers. A raw pointer, as you correctly pointed out, tells your brethren programmers nothing. Can they pass an automatic variable? Will the function attempt to take ownership of the object? A raw pointer says nothing. It is mute...dumb! A smart pointer, however, tells the faithful exactly what is expected of the pointer.

"Let us first look at this function f(). Which smart pointer tells other scribes 'I will tend to this member of your flock from now on?'" Neither Kerry nor I moved. Wendy whispered, in a loud stage whisper over the cubicle, "Transfer of ownership!"

"Ah, right," I said, "The auto_ptr...I mean, the blessed auto_ptr tells the, uh, faithful and initiated that the function will tend the pointer as if it were a member of its own flock. The calling function need concern itself with the pointer no longer."

"Well said, my apprentice. Indeed, it would be a mistake for the calling function to attempt to use the auto_ptr after having passed it into a function. Now, let us turn to the function g(). This function accepts a pointer to an object, and the function cares not about the object's storage or lifetime. Which smart pointer can tell the faithful 'I care not whence this pointer came, nor will I attempt to gather this pointer into mine flock?'"

That one stumped me. I waited for Wendy to pop up with her suggestions, but she remained silent. I stole a glance at Kerry. He wasn't in much better shape than me.

"Fear not, my apprentices, for it was a trick question. Alas, neither the the blessed auto_ptr nor the Boost smart pointers have such semantics, for they all are designed to assume ownership of dynamically allocated pointers. This leaves us with a few options. First, and least desirable, we may leave the function alone. The drawback here is that you are not communicating with your fellow faithful scribes, and they will be left in the same crisis of faith as before.

"Next, we may modify the function g() so that it accepts a Boost smart pointer [1], such as a shared_ptr. The drawback here is that you are no longer able to pass the address of a local variable as the argument — you must dynamically allocate the objects.

"Third, we could use Loki's SmartPointer [2], and write an ownership policy that does nothing:

template < class P >
class NonOwned
{
public:
    static P Clone ( const P& val )
    {
        return val;
    }
    static bool Release(const P& val)
    { 
        return false; 
    }
    enum { destructiveCopy = false };
    static void Swap(NonOwned&)
    {}
};

"The member function g() now becomes:

void g( Loki::SmartPtr<U, NonOwned> u );

"Finally, we could create a very simple template wrapper whose sole purpose is to tell other scribes that you do not care about the source of the object, nor will you attempt to take ownership of it. The template would be similar to the auto_ptr, without the ownership semantics or the auto_ptr_ref. Something along these lines," she wrote on the whiteboard:

template<class Type>
class NonOwnedPtr
{
public:
  NonOwnedPtr(Type *ptr = 0)
    : myptr_(ptr)
  {
  }
  Type& operator*() const
  {
    return (*myptr_);
  }
  Type *operator->() const
  {
    return (myptr_);
  }
  Type *get() const
  {
    return (myptr_);
  }
  void reset(Type* ptr = 0)
  {
    myptr_ = ptr;
  }
private:
  Type *myptr_;
};

"This template is not limited to dynamic objects. You may use automatic or static objects with it, as you wish. You will notice, my apprentices, that this class relies on the compiler-generated constructors, assignment operator and destructor. That is because this template imposes absolutely no ownership semantics on the object it points to.

"In virtually all respects, this template is a simple pointer — but not a mute one. It lets you preach to your fellow programmers the intent of the function — that is to say, that your function desires a pointer to an object, but does not covet that object to take ownership of it. As I have often preached and will say to any who will hear, the main purpose of source code is to communicate your intent to your fellow scribes."

The Guru turned and glided away. I looked at Kerry. "Well, there you have it," I said. "Which approach will you take?"

Kerry took over the keyboard, and quickly updated the class:

class T
{
public:
  void f( std::auto_ptr<U> );
  void g( Loki::SmartPtr <U, NonOwned> );
};

I nodded. "Wise choices, apprentice. Now if you will excuse me, I must return to my meditations," I said as I slid back the keyboard and resumed work.

References

[1] The Boost library, http://www.boost.org/.

[2] Modern C++ Design, Andrei Alexandrescu, Addison-Wesley, 2001.


Herb Sutter (http://www.gotw.ca/) is convener of the ISO C++ Standards committee, author of Exceptional C++ and More Exceptional C++, and one of the instructors of the C++ Seminar. In addition to his independent writing and consulting, he is also C++ community liaison for Microsoft. Jim Hyslop is a senior software designer with over 10 years of programming experience in C and C++. Jim works at Leitch Technology International Inc. where he deals with a variety of applications ranging from embedded apps to Windows programs. He can be reached at [email protected].



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.