Conversations #1

Home Blog Talks Books & Articles Training & Consulting

On the
blog
RSS feed November 4: Other Concurrency Sessions at PDC
November 3
: PDC'09: Tutorial & Panel
October 26: Hoare on Testing
October 23
: Deprecating export Considered for ISO C++0x

Re-membering auto_ptr
Copyright © 2000 Jim Hyslop and Herb Sutter

 

I had met Jeannine only the day before, in the crew rotation staging area now far below us. "I'll always remember my first job," I told her after the flight attendant walked past, checking our belts.

"What about it?"

"The senior programmer on my project," I smiled, remembering. "She was an odd duck. We called her the Guru. Management didn't like to assign new programmers to her team; I was the only one of four hires that year to last through the probation period."

Jeannine tilted her head and was about to ask a question when the last chime sounded and the rumbling acceleration took hold, cutting off conversation for a few minutes. At the end of the burn, as we left orbit, I told her the story of my second day at work.

- - - - -

We were programming in early C++. On the afternoon of my second day, tired of reading employee handbooks, I wrote a utility class that contained a raw pointer as one of its members:

#include "xStruct.h" // definition of struct X

class xWrapper
{
  X* xItem;
public:
  xWrapper() : xItem(new X) { }
  ~xWrapper() { delete xItem; }
  void dump() { /* dumps xItem to cout */ }
};

Of course, the program using this class kept crashing intermittently with memory corruption, because I'd violated the Law of the Big Three: Whenever you provide any one of a destructor, copy constructor or assignment operator, you will generally need to provide all three.[1] "So," said I to myself, said I, "I have to handle copying and assignment. Simple... auto_ptr already has a copy constructor and assignment operator, so I'll use that." (You've heard of the auto_ptr in the original C++ library, right?)

Since the auto_ptr automatically deletes the object it points to, all I had to do was change the type of xItem and remove the delete statement in the destructor -- auto_ptr would take care of the rest, right?

class xWrapper
{
  auto_ptr<X> xItem;
public:
  xWrapper() : xItem(new X) { }
  void dump() { /* dumps xItem to cout */ }
};

Unfortunately, my program was still crashing, this time because it was trying to dereference a null pointer. I had been trying to puzzle out the problem for about half an hour when the Guru, thin as a rail, decided to walk by, carrying a thick book open in one hand. She did that a lot... show up at propitious times, I mean; I think it was some sort of prescient thing. Downright spooky, actually.

"Uh, what're you reading?" I asked her, pointing at her book to deflect attention away from my screen and hoping she would go away.

The Guru blinked. "The writings of Josuttis," she said softly, marked her page, and closed the book. "What's that you have there, young one?"

"I'm having problems with this wrapper class I'm writing," I admitted. "I'm using an auto_ptr member, but in my test harness its pointer keeps getting reset to null for some reason."

"Show me your writings," the Guru said. I showed her the screen. "Ownership," she said immediately, after barely a glance.

It was my turn to blink.

"Ownership, child; your problem is ownership semantics. No person can serve two masters, and no pointer can serve two auto_ptrs."

Her words, although certifiably strange, made me realize my mistake. "Okay, right," I nodded. "When you copy an auto_ptr, the original one relinquishes ownership and gets reset to null. My xWrapper copy constructor reuses that default behavior, so the original xWrapper object's auto_ptr is being reset, and when I try to access it I'm dereferencing a null pointer."

"Correct," the Guru agreed. "You have done well to reuse the true tools of the Standard, but you must take care with them. For xWrapper, you must still manage xWrapper copying and assignment yourself."

"But I can't implement them in terms of auto_ptr's own versions because those won't do the ri--- Oh. I get it. I'll use auto_ptr's dereference operator to access the owned objects." I quickly wrote out the two functions:

xWrapper::xWrapper(const xWrapper& other)
  : xItem(new X(*other.xItem))
{ }

xWrapper& xWrapper::operator=(const xWrapper &other)
{
  *xItem = *other.xItem;
}

"Hey, cool." This, I liked. "I don't even need to check for self-assignment in the assignment operator."

"That is correct."

I should have stopped then and kept my mouth shut, but I wasn't that smart yet. "auto_ptr sure is easy to misuse. If only it could have told me I was trying to transfer ownership when I didn't expect that to happen..."

"Peace!" interrupted the Guru. "The fault is not with auto_ptr in this instance. You should have said that you did not want the auto_ptr to be copied, had that been your desire."

"But how? That's not possible."

"Ah, but it is. Remember the blessings of const-correctness. The way to state that an auto_ptr is immutable is to make it const. Had you made the member a const auto_ptr, the compiler would not have been able to silently perform the copy of the xWrapper object. Alternatively, had you used something like the strict_auto_ptr from the Second Revisionist rendering of Cline 30:12, the compiler would not have been able to mistakenly generate incorrect xWrapper copying and assignment. Of course, in this case making it const would have been simpler and sufficient."[2]

She reopened her copy of Josuttis and resumed reading as she started to walk away, still talking to me absently, both she and her voice gradually drifting away: "A word of caution, my child... auto_ptr is a useful tool, but as you have discovered, it is not a panacea. Read and meditate upon Josuttis chapter 4.[3] You must never instantiate a Standard container of auto_ptr, such as vector<auto_ptr>, because auto_ptr does not meet the copying and assignment requirements of the Standard. Furthermore, never attempt to use auto_ptr to point to an array of objects, for the auto_ptr's destructor uses non-array delete to delete the object it owns; for an array of objects, use a vector instead. The library..."

But then she turned a corner, and was gone. It was only my second day; I wondered, not idly, if I ought to update my résumé while I could still pretend this job had never happened.

- - - - -

"Weird lizard," Jeannine opined, sipping coffee as we exited the Terran local traffic control area, still gaining velocity. "So, did you leave?"

"She was, but no. I'm not sure why," I added honestly. "A few run-ins like that, and I was ready to leave during probation like the rest. I guess he grew on me, though. Didn't you ever work with a quirk like that?"

"Mmm. Some. I suppose."

It was not the last time I would speak with Jeannine about the Guru, or about more pleasant things.

 

Notes

1. M. Cline, G. Lomow, and M. Girou. C++ FAQs, 2nd ed. (Addison-Wesley, 1999).

2. Ibid., FAQ 30.12, pages 426-8.

3. N. Josuttis, The C++ Standard Library (Addison-Wesley, 1999).

Copyright © 2009 Herb Sutter