Andrei Alexandrescu discusses smart pointers, from their simplest aspects to their most complex ones and from the most obvious errors in implementing them to the subtlest ones — some of which also happen to be the most gruesome. Also, learn how to throw an exception, how to associate handlers, or catch clauses, with a set of program statements using a try block, and how exceptions are handled by catch clauses, exception specifications, and design considerations for programs that use exceptions.
I just finished reading a couple of Novice C++ books after having picked up the language without any formal learning regimen. I had to unlearn a lot of bad habits. One thing I noticed most C++ books stear clear of is the topic of smart pointers. Most allude to their existence but don’t want to get into the topic, even though they may get into iterators from the STL that are almost as confusing to the novice. I’ll definitely keep this article handy for future reference.
Eugenia, we need more articles like this. You actually learn loads from them.
Exceptions are one thing Java certainly excels at when compared to C++, albeit only due to Java’s garbage collected nature. The use of dynamic memory in C++ exception handling can be error prone, sometimes resulting in memory due to poorly written exception handlers.
Many here will be familiar with the fact that I have many issues with Java, and prefer C/C++ instead, however C++ exceptions are, at best, a mixed blessing.
Actually that is why the smart pointer article is important. The only way in C++ to write safe code while using exceptions is to never ever use regular pointers for heap-allocated objects in a function that is responsible for deleting it. If you don’t take care of that you will sooner or later have a problem. If you use smart pointers all the time exceptions are painless.
Anonymous: I just finished reading a couple of Novice C++ books after having picked up the language without any formal learning regimen. I had to unlearn a lot of bad habits. One thing I noticed most C++ books stear clear of is the topic of smart pointers. Most allude to their existence but don’t want to get into the topic, even though they may get into iterators from the STL that are almost as confusing to the novice. I’ll definitely keep this article handy for future reference.
Actually, this is essentially just one of the chapters in his book rewritten in web format. You ought to check out Modern C++ because it has a lot more information on policy-based class template design and of course on Loki, a very small but powerful template library. That book is well worth the money!
Bascule: Many here will be familiar with the fact that I have many issues with Java, and prefer C/C++ instead, however C++ exceptions are, at best, a mixed blessing.
I agree with Tim Jansen. If you have to worry about dynamic resources when throwing exceptions, something is wrong with your coding. As a general rule one should seek to wrap each dynamically allocated resource into its own class even if that class is just the std::auto_ptr. This goes for class data members, too, as its constructor might wind up throwing an exception. In fact, the only place you never have to worry about exceptions is in a destructor because throwing an exception from a destructor is a very bad idea.
I just wanted to point out that, although they can be very convenient, auto_ptrs are not a silver bullet. The key things to notice about them are that (a) they cannot be cast to a regular pointer type and (b) they are not copy-constructible. (a) means that all of the functions you call must be designed to be able to accept an auto_ptr instead of a standard C++ pointer (not an easy thing if you want to use library functions). (b) means that you cannot store auto_ptrs to objects in any STL containers, as the STL requires the objects stored in its containters to be copy constructible. The auto_ptr is not copy-constructible because the semantics of an auto_ptr assignment is transferral of ownership, so you may only have one auto_ptr to an object at a time. As a consequence, this means that functions that use the auto_ptr must pass it by reference or return a new auto_ptr. If you really are worried about memory management, it’s probably better to roll your own reference-counting scheme, which can be done fairly simply with some static class members & functions–this approach will suffice as long as you don’t have to worry about circular references (a.ptr points to b and b.ptr points to a, for example). If your structures are that complex, you can get one of the excellent garbage collection frameworks (such as Boehm) that have been written for C++. In almost any case, there’s a better way to do it than auto_ptr.
Just my $.02…
KOMPRESSOR
I just started programming C++/STL a few months ago and I just the love C++ and especially the STL containers. Coming from a Java background I gave C++ another chance and found it much more capable for my needs. However, I found auto_ptr extremely lacking so I wrote some of my own much more useful “hybrid” smart pointers for STL use.
*Acts like a pointer and thus minimizes copies
*Destroys obj in destructor or on assignment
*Wraps the objects comparison operators (So compares happen on overloaded object operators, not on the ptr)
*Uses reference counting to only destroy obj when count hits 0
The class you describe is very like what I had in mind (and have used in the past). It can be a very useful technique, depending on your needs.
You do need to be careful with your smart pointers in the case of circular pointers or self-reference; objects that point to themselves will always have a reference count of 1, regardless of whether or not they are still “alive,” and it’s possible to get a closed loop of objects all keeping each others’ reference counts up even after they are unreachable from the rest of the program.
I congratulate you on your re-discovery of C++ I have never really liked the fact that Java insulates me from certain aspects of the program’s operation, and I think that generics are truly a more interesting paradigm than classical OO.
KOMPRESSOR
This kind of tutorials just points out how inadequate C++ just is as a language. It is one of these language where you spend more time learning about coding discipline, methodologies and add-ons such as smart pointers which are just fixes on a language design that was bad in the first place, than you spend time solving actual original problems.
I mean “smart pointers” don’t do anything. They just emulate what any decent language does, that is managing your allocations. And they add an overhead that prtly nullifies the performance of compiled code that C++ coders imagine they have.
C++ is a mess and a badly designed language. It does not have any complete, standard library of its own (STL is never implemented the same in two different compilers, and brings an horrible overhead). But more importantly it is inappropriate. Objective C is a better “C with objects”. Java and C# are similar but much cleaner and efficient. And dialects of Scheme, List and ML such as Bigloo or Objective CAML are the best general purpose software languages out there. They offer all functionalities that you can imagine, from multithreading to GUIs, are compiled into code just as fast as native C++, they manage your allocations, your typing, have better modular compilation. They render C++ obsolete.
KOMPRESSOR: I just wanted to point out that, although they can be very convenient, auto_ptrs are not a silver bullet.
Exactly. I don’t recall anyone claiming that it was a silver bullet, but I agree with you anyway.
KOMPRESSOR: (a) means that all of the functions you call must be designed to be able to accept an auto_ptr instead of a standard C++ pointer (not an easy thing if you want to use library functions).
No, it just means that you use std::auto_ptr <T>::get () to get the “raw” pointer.
KOMPRESSOR: (b) means that you cannot store auto_ptrs to objects in any STL containers, as the STL requires the objects stored in its containters to be copy constructible.
Yes, basically.
KOMPRESSOR: As a consequence, this means that functions that use the auto_ptr must pass it by reference or return a new auto_ptr.
Writing functions that explicitly take an auto_ptr <T>& is a really bad idea because it forces everyone who calls that function to use auto_ptr <T>; hence, you cannot pass in addresses of objects on the stack or objects that are being managed by smart pointers other than std::auto_ptr. Genericity is a Good Thing and not something to be sacrificed unnecessarily.
KOMPRESSOR: If you really are worried about memory management, it’s probably better to roll your own reference-counting scheme, which can be done fairly simply with some static class members & functions–this approach will suffice as long as you don’t have to worry about circular references (a.ptr points to b and b.ptr points to a, for example).
No, it’s generally better to pick up a library of class templates that have already been tested and debugged. Boost’s shared_ptr and Loki’s SmartPtr (the one described in the article) spring to mind. Rolling your own is a bad idea if it is possible and practical to use someone else’s code. Of course if you want to write your own code for learning purposes that’s fine, too.
KOMPRESSOR: If your structures are that complex, you can get one of the excellent garbage collection frameworks (such as Boehm) that have been written for C++.
If your structures are that complex, you need to think about redesigning your application.
KOMPRESSOR: In almost any case, there’s a better way to do it than auto_ptr.
That’s quite different than what you started out trying to prove, and I don’t agree that std::auto_ptr is nearly useless. It is an excellent habit to return auto_ptr <T>’s from class factory functions. It also works very well when a class has one or more dynamically allocated data members. Additionally, there are times when one needs to allocate local objects on the heap and one doesn’t want to have to write unwieldly try-catch blocks and duplicate code just to make sure that the deallocation occurs.
KOMPRESSOR: I congratulate you on your re-discovery of C++ I have never really liked the fact that Java insulates me from certain aspects of the program’s operation, and I think that generics are truly a more interesting paradigm than classical OO.
Actually, I think that the generic and object-oriented programming paradigms work well together.
Loic HG: This kind of tutorials just points out how inadequate C++ just is as a language. It is one of these language where you spend more time learning about coding discipline, methodologies and add-ons such as smart pointers which are just fixes on a language design that was bad in the first place, than you spend time solving actual original problems.
Would you mind going someplace else? I don’t mind disagreeing with people who are actually interested in intelligent conversation, but just inserting a bunch of “your language sucks” comments into a nice discussion really gets on my nerves. Yes, I’ve done it myself, and I suspect many others have, too, but that doesn’t make it right.
Loic HG: I mean “smart pointers” don’t do anything.
Yes, they do. They take ownership for a resource that must be allocated dynamically.
Loic HG: They just emulate what any decent language does, that is managing your allocations.
Most of the decent programming languages have “garbage collectors.” I put that in quotes because garbage collectors don’t. Collect garbage, that is. Most of them just let it pile up until it fills available memory at which time the computer tends to thrash around a lot until the garbage is cleared away.
Loic HG: And they add an overhead that prtly nullifies the performance of compiled code that C++ coders imagine they have.
What a stupid statement! It actually nullifies the performance advantage that we don’t actually have? Which is it? Does compiled code have a performance advantage, or does automatic dynamic resource management not add overhead? You cannot have it both ways.
Loic HG: C++ is a mess and a badly designed language.
I agree, but for vastly different reasons.
Loic HG: It does not have any complete, standard library of its own (STL is never implemented the same in two different compilers, and brings an horrible overhead).
Are you trying to be funny or something?
1. C++ does have a standard library of its own. It is the library that is specified in the C++ standard, which you may download for $20 if you want to read up on the language that you are bashing.
2. The fact that the standard library is never implemented the same in two different compilers is normal. Peruse the source code for a Java VM sometime, and you will see that one doesn’t build a new VM just by copying and pasting “official” code from Sun. The C++ standard library is in wide use and it is extremely useful.
3. The C++ standard library is well-known – at least amongst non-trolls – for its low overhead and excellent performance. Yes, there is a compile-time overhead, but that’s what you get for reducing all those unnecessary run-time type checks and messy casts.
Loic HG: But more importantly it is inappropriate.
Inappropriate? For what?
Loic HG: Objective C is a better “C with objects”.
I think the expression is C with classes. I do not understand why people use that expression, as C++ offers so much more than just introducing classes to C.
Loic HG: Java and C# are similar but much cleaner and efficient. And dialects of Scheme, List and ML such as Bigloo or Objective CAML are the best general purpose software languages out there. They offer all functionalities that you can imagine, from multithreading to GUIs, are compiled into code just as fast as native C++, they manage your allocations, your typing, have better modular compilation. They render C++ obsolete.
*cough* Yeah, sure they do. *smiles and motions calmly to the men in the white coats*
I love taking advantage of the ability to take advantageof manu levels of C++. Sometimes a project dictates nbothing thjat might add exe bloat or slow downs, so we dont use them.
My most recent project I converted a C program into a big classs, seperated it out, added exceptions, and then added STL. Every step along the way we were able to compile and run. Also we directly used a C library for one of our interfaces (NCurses).
Since none of my earlier projects even called for STL or exceptions, Ive been having to read up on them, and this article helped on stuff my book left out (try/catch as the funtion body, putting throws in the prototype) Thansk for posting it.
As for those who say C++ is bad, I look at all the projects I ahve done and the flexibiltyIve had and I think its great. I have been using STL to help get rid of my points so as to not cause problems with exceptions. I also use a Java like way of doing exceptions with a class heirarchy and stuff.
Some in a previous article talked about how exceptions are bad because you dont know where exactly it was thrown so you do not know how much to undo. If you thow different kinds of exceptions, or use a class and put different information in it, it is easy to tell where it was thrown. Yeah, of course you can’t if you say catch (…). I would only do that in my main block just to give a nice error message for unknown exceptions