''Smart pointers aren't very smart.'' A good smart pointer design works well for many things, if you use it for what it was designed for. There are situations in which SmartPointers are problematic, but they don't arise that often, and there are other patterns that can solve the resource management problems in those cases (e.g. letter/envelope designs). But in many situations, smart pointers work just fine. Use auto_ptr when you're not sharing references to an object, use the Boost pointers (some of which are proposed for future standardization) for more complex situations. If you have general issues with smart pointers that you'd like to discuss, SmartPointer would probably be a good place for it. -- DanMuller ''Every implementation of smart pointers that I've seen will fail in some contexts. Just because it works now doesn't mean it will work later if the context changes. I know of no reliable way to automatically guarantee the right implementation as the code changes. Manually releasing the resources seems much more reliable. Perhaps I have an illogical fear of smart pointers. Perhaps they've become smarter since I last used them. My experience was that they eliminated certain classes of memory leaks, but not all, and the leaks they allowed became even more difficult to find. -- EricHodges'' There are lots of bad home-brew implementations around, so your fear might not be illogical, just misdirected. I've had no troubles with recent implementations of std::auto_ptr or boost::shared_ptr, the two that I have the most experience with. I also used a home-brew that was based on a very careful adaptation of code in "Effective C++" (I think) which also worked well. They can't eliminate all problems - e.g. if you use a reference-counting pointer, cycles will still cause you problems - but it's been a long time since I've had any trouble that could be attributed to the smart pointers themselves. It's also been a long time since I've had any non-trivial memory leak problems. And they make the code one hell of a lot cleaner, so it's easier to find any remaining mistakes. -- DanMuller ''I don't like having to worry about cycles (or anything else) breaking a smart pointer. There may be no cycles when I write the constructor, but I don't want to have to think about all of the constructors that might be affected when I introduce a cycle later. I don't know of any way to automatically detect those errors, so I'd rather explicitly release resources. -- EH'' Umm, that's really a silly objection. Cycles can cause problems when using reference-counting, whether you use smart pointers or not. The direct alternative to using reference-counted smart pointers is to do the reference counting by hand. Introducing a cyclic reference in such a situation would be a programming error that is entirely independent of the use of smart pointers. The smart pointers would relieve you of the tedium of managing reference counts, so you could spend more time making your code correct and avoiding things like accidental cycles. -- DanM With the exception of things like cycles not being a bug, and being handled by pool-based resource allocation/deallocation a pool at a time rather than item at a time; there are situations where that's a better approach than smart pointers. It's not clear that that's what EH meant, though. -- Doug Sure, I wasn't referring to situations where cycles are expected and catered to. Reference counting of any variety might be a poor choice in such cases - and then you wouldn't use a reference-counting smart pointer. Now, if your program changed such that reference counting was no longer appropriate, it may be easier to remove it by changing the pointer declarations than by editing constructor code. (You might have to edit the allocation expressions in any case - unless specializing new is appropriate, depends on circumstances.) In fact, you might easily replace the reference-counting pointers with smart pointers that cooperate with a pool to diagnose dangling pool resource references in debug builds - another win for smart pointers. -- DanM True. Although that strays into the area of smart pointers purely for debugging, which generally need not be as bullet-proof as smart pointers central to algorithms in production release, which makes a difference, since we're all agreed that home-brew smart pointers are often more flawed than the standard mil-spec smart pointers. But yes, they can be '''great''' for debugging any number of kinds of things. -- Doug Cycles are always expected, to some degree. I don't want to work in a code base where I have to be vigilant against cycles (or any other situation that could break a smart pointer.) -- EH Am I not writing clearly? Cycles have nothing to do with smart pointers. I originally mentioned cycles as an example of a problem that smart pointers don't solve. Cycles are not caused by smart pointers, nor do cycles cause smart pointers to "break". -- DanM I agree with Dan, Eric, and although I agree that needing to be vigilant about trivial issues is something to be avoided, I disagree that cycles are always expected. It depends on the context. If they occur when they should not, that is a huge bug that needs to be hunted down, not a trivial issue, and smart pointers can actually '''help''' find such things. We're not saying smart pointers are always the way to go, note, particularly because we're not just talking about defining smart pointers as doing no more and no less than reference counting. They can be smart in arbitrary ways. If you are just trying to say that smart pointers aren't always the right solution, sure, I think we'd all agree. -- Doug I don't want to focus on cycles (they are just one example of how one type of smart pointer can be defeated), but I don't seem to be making myself clear. I want to use cycles when I need cycles, or when the code needs cycles if you will. If I add an A and A has B has C has A, then I'll use cycles. The smart pointer implementation choice made by the author of B shouldn't stop me, or even slow me down. I should be able to trust that if B's constructor fails it won't leak memory. From everything I've seen there's no single implementation of smart pointers that's appropriate for all contexts. They all have gotchas. Because of that I think they are only rarely the right solution (when you absolutely positively know the gotchas will never get you.) -- EH ''"... there's no single implementation of smart pointers that's appropriate for all contexts..."'' Of course not. You must ask yourself: "What's '''smart''' about SmartPointer''''''s?" Use of a smart pointer replaces some bits of repetitive code. The code that it replaces would have done something specific; managed reference counts, deleted a referenced object when the pointer variable goes away, intercepted calls to provide tracing, etc. You choose a smart pointer implementation that meets your requirements. If your needs are unusual, you can roll your own - it's possible to do so, with care. In any case, the smart pointer usually places no more restrictions on you than the code that it replaces. In your example, if the author of B chose an implementation, with or without smart pointers, that is inimical to cyclic references to objects of class B, then that's a limitation of class B that has to be addressed in B's implementation if you can no longer tolerate it. When making such a change, you're as responsible for understanding the capabilities and implications of the smart pointer class as you would be for understanding direct in-line code if no smart pointers were used. You have not raised any objections against smart pointers that are not equally valid for the code that they replace. Smart pointers relieve code repetition, just as functions do; but they're more powerful than functions by virtue of automatic destructor calls, a point which is tremendously useful when programming in the presence of exceptions. std::auto_ptr and boost::shared_ptr address situations that are very common, relieving you entirely from writing some code in the cases where they're applicable. When conditions change, you might have to change the type of smart pointer used, write your own, or abandon their use altogether. In the absence of smart pointers, you would instead have to review and possibly change all the code that exists due to their absence. The only mistake you can make with smart pointers is to have a misunderstanding or lack of understanding of the them, somehow expecting them to automatically adapt to changing requirements in ways that inline code couldn't. That would indeed be folly - but blaming the smart pointers for this would be a clear case of ShootingTheMessenger. -- DanMuller I don't blame the smart pointers for that. I just don't use them. I'd much rather write code that doesn't have to be reviewed when conditions change (as in my first example of explicitly releasing resources when a constructor throws an exception). -- EH Your example introduces unnecessary code duplication and verbosity. This multiplies rapidly in real-world code, where you might have, for instance, situations that involve re-allocating the resource in a member function. Using the original example that prompted this discussion, give an example of a change in conditions that would reasonably lead to an assumption that inline code need not be reviewed, yet the use of a smart pointer would have to be. Your justification for your choice is far too vague for me to take seriously. -- DanMuller See the "A has B has C has A" example above. If the constructors explicitly release their resources on exception then no code has to be reviewed. If reference counting smart pointers were used then code would have to be changed when the cycle was introduced. -- EH Yes, and if reference counting inline code were used instead of smart pointers, it would have to be changed, too. Seems obvious to me. Why is this not getting through? -- DanM Because no one suggested using reference counting instead of smart pointers. I suggested releasing the resource explicitly on exception in the constructor. Why is that not getting through? -- EH Well then why are you comparing apples and oranges? If you don't want reference counting behavior, why would you use a reference-counting smart pointer? This is not making sense. -- DanM Read what I've said again, please. There may be no cycles in the original code, so a reference counting smart pointer may be perfectly reasonable. This isn't about the original code, though, it's about the consequences of changing the code later. Cycles vs reference counting smart pointer implementations is just one example of how a smart pointer can fail. Perhaps algebra will make it more clear. For any smart pointer implementation X there's a condition Y that causes X to leak. Therefore X is used when Y is not present. When Y is introduced X must be altered. If I avoid smart pointers and explicitly release resources on exceptions in constructors then everyone who uses my code is free to introduce Y without having to find all of the possible Xs that need to be altered. Smart pointers (all the implementations I've seen) make code more brittle because there is no implementation that's right for all situations. Each implementation places restrictions on the code around it. -- EH (You wrote that while I was writing the below, but I suspect my comments may still be just as apropos:) I agree that at this point it becomes clear that somehow we're talking past each other. Let's regroup and clear up the confusion. I suspect that the root of this stems from EH having had '''only''' bad experiences with "smart pointers", where Dan and I have had both good and bad experiences - this seems to have been agreed by all of us above, but it would obviously color EH's interpretation of the subject. I was a relatively early adopter of C++ (not the earliest when it was just "C with classes", but before it had exceptions, and certainly before it had templates, let alone STL), so my 1st experience was in inventing the notion of reference-counting pointers independently (probably thousands of people, at minimum, have independently invented them). Because it was my invention, I didn't hate it, but it was a serious nuisance to get it '''right''', and I learned a lot more about C++ in the process. I don't know who coined the term "smart pointer", but at some point there was a lot of word-of-mouth buzz about it, and everyone was implementing their own version, and mostly not testing (etc) enough to find their own bugs, and then some of these really buggy smart pointers got into somewhat wide use, and a lot of people were burned. Somewhere along in that timeline templates were added to C++, and templatized smart pointers started to get implemented, which added a new set of powers and a new set of bugs. Boost may have been the first to offer a really widely used templatized smart pointer of industrial strength (common design and implementation errors avoided); I don't recall. But there certainly was a first such, and up to that point, zillions of programmers had been burned by various kinds of bad smart pointers, and many have simply thought "once burned, twice warned", so perhaps EH is in that camp. If so, I sympathize. The second thing is that there are differences in terminology. To some people, "smart pointer" just means reference-counting garbage collection. To me, however, it means something vastly more general: a pointer that intercepts some or all operators and does something, '''anything''', "smart" with them, for instance, set/unset a lock, print debug info, consistency-check a large data structure - whatever. I haven't kept track of whether that broader sense of the term "smart pointer" is widespread or whether I am behind the times with that usage. Opinions and citations welcome. Now, I've been burned by more general smart pointers, too, in particular I have war stories about autolock smart pointers people have written. But they were all fixable. I hope that we have simply been having some confusions over differences in terminology, and if so, perhaps that will shed some light on at least where I'm coming from on that topic. I am not insistent on any particular definitions, but let's start by agreeing on what we're talking about. -- DougMerritt I think I'm using the same definition of "smart pointer" as Dan. I'm using the one given on the SmartPointer page. I don't think our disagreement is over terminology. If there were a single implementation of a smart pointer that could be used safely and efficiently in all situations, I would use that. The fact that there isn't (that I know of, so far, etc.) makes me wary of them. The circumstances that caused a certain smart pointer implementation to be chosen aren't readily apparent to everyone who will be working in the codebase. Changes in those circumstances don't automatically set off alarm bells that cause the next appropriate smart pointer implementation to be chosen. They just introduce a leak. -- EH This is all well and good; I agree with the summary at the end of the page, and everything Doug's written, except for the idea that Eric and I are talking past each other. I think I understand what Eric's saying, and I believe him when he says he understand me. But Eric's example and his extrapolations from it still seem completely illogical to me, especially if he is in fact using the same definition of smart pointer. So pardon me if I beat a horse that is already looking decidedly ill, but I'd like to get to the bottom of this. Eric recommends using explicit deletion. He compares this to a reference-counting smart pointer implementation. Now, since we agree that "smart pointer" is a far more general concept than simple "reference-counting smart pointer", there's no reason to compare Eric's proposed implementation to one that uses such; they're not by any means equivalent. The proper comparison would be to a smart pointer that does not reference-count, and that simply manages deletion for you - namely std::auto_ptr. Also, and more importantly, the assertion that a straightforward explicit-deletion approach is invulnerable to problems if cycles are introduced is not true. You will get double-deletion problems, or recursion into destructors, which have to be guarded against by code that would not have been written in the first place if recursion were not anticipated. (You would have those problems using a std::auto_ptr, also.) -- DanM Let's ignore reference counting. You introduced it as an example, but I think we can generalize this to any smart pointer implementation. You introduced the topic of smart pointers with this recommendation: ''"Instead of declaring t as a simple pointer, make it a SmartPointer, e.g. a std::auto_ptr<>, or one of the BoostLibraries' robust implementations. Even if you throw an exception in a constructor, data members that have already been constructed will be properly destroyed."'' That last clause isn't entirely correct. The data members will be properly destroyed '''only if''' the correct smart pointer implementation is used. The implementation decision rests on code inside and outside the constructor. There is no automatic way to make sure the correct implementation will be used as the code outside the constructor changes. Then someone suggested: ''"Or, if the constructor does something which will throw, catch the exception within the constructor (re-throwing as necessary)."'' As far as I can tell, that's going to work no matter how the code outside the constructor changes in the future. If you can explain how that solution is not more reliable than using smart pointers, please do so. -- EricHodges For the specific example that was given on MemoryLeakInCpp, std::auto_ptr, as recommended, would have done exactly what was wanted. But the example was obviously not complete code; hence I hedged and mentioned other smart pointer implementations, the choice depending on other desired behavior not made explicit in the example. In fact, either an auto_ptr or a reference-counting pointer would work precisely correctly in the constructor, regardless of what other behavior the program has, because the resource was allocated in the constructor and released in the constructor if an exception occurs. (There is no opportunity in the constructor, as shown, for the resource to become shared.) * But I'm not just talking about the code as shown. I'm talking about the future of that code. -- EH * I am also talking about the future of the code, ''and'' the parts of the code that are obviously omitted from the example, as should have been abundantly clear from the paragraph after next. -- DanM Leaving out the smart pointers does not ensure that "what you see is what you get", as you imply. The class being allocated could override new and delete, for example. When changing code, there is no substitute for understanding each of the classes involved in the code. Whether you use smart pointers or not, for this trivial example the difference is small, as is typical for toy examples - using an auto_ptr would save two deletes, a try/catch block, and a conditional in the destructor. (Note that the example must be changed to initialize t in the initializer list, otherwise the test in the constructor is unreliable; the allocation of Thing could occur there, or t could be initialized to zero.) As soon as you go beyond the trivial example, where the resources will be used and possibly shared in additional ways, or when the constructor gets longer and has more possible failure points, or when you add more data members that need to be managed similarly, then the savings in code bulk and complexity becomes very significant. ''Especially'' when programming in the presence of exceptions, automatic deletion, automatic reference count reduction, etc. provide ''large'' gains in reliability, and in code legibility by reducing the number of try-catch constructs needed. Even in styles that eschew exceptions, smart pointers reduce the number of explicit error-check-and-recovery constructs needed, again leaving the code easier to read. I worked on a multi-million line program for several years where we gradually moved to fairly rigorous use of smart pointers and other techniques involving automatic release of resources. Leaks went from a weekly occurrence to something you ran into once in a few months, at most. That was all the proof I needed. In my experience, there are a large number of frequently-used patterns - letter/envelope arrangements, acyclic graphs, resource allocators handing off ownership to callers, to name a few - that are trivially handled by using commonly available smart pointer implementations. The introduction of changes that break these is rare, and usually quickly diagnosed. I've only run into one problem with a cycle in six years of working with this program, for instance, and that was introduced unintentionally. -- DanMuller '''"When changing code, there is no substitute for understanding each of the classes involved in the code."''' I think this is the root of our disagreement. If I have to understand each of the classes involved (to the degree you imply above) then there's far too much coupling in the code. In the constructor exception scenario we've been discussing, smart pointers introduce more coupling than explicitly releasing resources because the smart pointer implementation choice depends on how the containing class is used, while the explicit release does not. -- EricHodges More coupling between what and what? Both smart pointer choice and inline code choice are decisions internal to the class that owns the data member, assuming the data member is not public, according to standard good practice in C++. Why does the smart pointer choice depend on how the containing class is used, any more than the choice of inline code depends on how the containing class is used? There's nothing magic about an explicit release; it's a choice, with specific behavior characteristics, as much as reference-counting code or pooled heap code would be a choice with different behavior characteristics. -- DanMuller Because the smart pointer implementation choice depends on code outside the containing class. For example, copying an object containing a std::auto_ptr nullifies the pointer. That's a dependency from a class member to every usage of that class. Every time I write "SomeClass a = b;" I have to wonder if b has an auto_ptr inside it. That's coupling and it makes baby Jesus cry. The explicit release doesn't impact decisions outside the constructor. -- EH Wanna bet? You seem to be assuming a default copy constructor. That choice, and the choice of using a bare pointer with explicit release, determines that a copy-constructed instance of the class will share t's Thing between two instances. Since your technique included straightforward explicit destruction of t's Thing in the destructor, your choices cause double-deletion of it. If you have pointers in your class, smart or not, you have to think about what copy construction and assignment do. (Class design is not as easy as most people think - if you have to know internal implementation details to figure out what a class's copy constructor does, the class designer failed.) Failing to do so introduces bugs. Your inline constructor code is as coupled to "external behavior" as a smart pointer choice is. (I quote "external behavior", because we're really still talking about an internal implementation detail. Whether copying, sharing, or transferring ownership of t's Thing has an effect on externally visible behavior is unknowable in this incomplete example.) -- DanM Huh? I suggested explicitly releasing resources on exception in a constructor. How does that lead to double deletion? -- EH Read again: in the ''destructor''. You can't talk about the management of t without discussing both ''construction'' and ''destruction'' of MyClass, as well as ''copying'' and ''assignment''. Let's examine this in a bit more detail. Let's assume what is perhaps the most common case - on copy or assignment, MyClass is supposed to allocate a duplicate Thing. Using bare pointers, the relevant members might look like this: MyClass::MyClass(const MyClass& other) : t(new Thing(*other.t)) { try { // other initialization, if any } catch (...) { delete t; throw; } } MyClass& MyClass::operator=(const MyClass& other) { if (this != &other) { // Allocate Thing first, so if it fails, we won't modify other state of *this. Thing *temp = new Thing(*other.t); try { // Other state modifications, if any. They should be arranged so that actual // state changes occur only if no exception is thrown. catch (...) { delete temp; throw; } std::swap(temp, t); delete temp; } return *this; } Obviously, if there's no intialization other than t, they end up fairly simple. Just as obviously, this would be pretty unusual. Perhaps less obviously, if there are multiple such resources in MyClass, things get considerably more complex than shown. Now the same thing using a std::auto_ptr: MyClass::MyClass(const MyClass& other) : t(new Thing(*other.t)) { // other initialization, if any } MyClass& MyClass::operator=(const MyClass& other) { // No check for self-assignment needed, unless there are performance // concerns. Self-assignment tends to be rare, though, so we don't bother. std::auto_ptr temp(new Thing(*other.t)); // Other state modification, if any. Same caveats as earlier. t = temp; return *this; } Both versions have to be examined carefully if changes in the pointer implementation or resource management policy occur. For instance, if it's decided that t's Thing can be shared and should be reference-counted, but copying MyClass should still create a new thing, then the first version (with bare pointers) needs to be extensively modified (or Thing's constructor has to be modified, depending on how you're going to manage the reference count). The second version (with smart pointers) needs to have the class declaration modified, and the temp variable declaration in operator=(). Which version requires more modification depends on the changes. Pretending that you don't have to look at all uses of t in MyClass in either case is just plain wrong, and dangerous. The smart pointer version contains less code, but depends on external code - std::auto_ptr, which is standard, well-documented, and should be in a C++ programmer's repertoire. I assert that the basic smart pointers in the BoostLibraries are similar - nearly-standard, well-documented, and should be well-known to every C++ programmer. -- DanM I don't see anything in there that explains how explicitly releasing resources on exception in a constructor leads to double deletion. -- EH Because there isn't anything about that in this last addition. Figure it out yourself by thinking about what the destructor has to do; it's obvious. Not to put too fine a point on it: I think you're being brain-lazy, and advocating an unjustifiable and dangerous laziness in code maintenance activities. -- DanM OK. I think you're being finger lazy by not answering my question. The destructor doesn't get invoked when the constructor throws an exception, so it doesn't have to do anything. No double deletion, not even single deletion. -- EH But what happens when the constructor succeeds, and you successfully make a copy, then delete both copies? -- DanM Life goes on, I assume. What answer are you looking for? -- EH Never mind, this is representative of the whole discussion, which has now become too tiresome. -- DanM Can anyone else explain what Dan was trying to say? -- EH Yes. He's saying he's giving up, because he already explained himself, and you didn't notice that he had because "you're being brain-lazy". You counter-accused him of "being finger lazy by not answering my question", but you are incorrect, he already answered, and wrote code to illustrate. Here's what I just did. I took his code, fixed typos and such, fleshed it out, and added the key code he discussed: an assignment of one instance of MyClass to another (while sharing an instance of Thing t, as he stated explicitly and as his code also assumes). And I added a debug print to the destructor for MyClass, which told me '''which''' instance of Thing t was being destructed there. Then I ran it, without throwing exceptions. The shared Thing t was destructed twice. Now, I'm just an onlooker, but I went to that amount of trouble. Dan expects you, as a participant, to go to at '''least''' that amount of trouble to explore the point, and I'd have to agree with him, that would be highly appropriate. Once you do so, you will see the double destruction in question. Which proves his point, unless and until you have a rebuttal '''after''' understanding the point in question. -- Doug I don't understand what "the point in question" is. I don't understand what his example code has to do with our discussion. I feel like I'm missing some key piece of information. Dan seems to be assuming something about copy constructors, but I can't follow his reasoning. I need some help. For instance, I don't know why Doug ran the code Dan posted, or why Dan posted it. The stuff about copy constructors seems entirely unrelated to the discussion of memory leaks in constructor exceptions. -- EH I ran the code to satisfy to myself that Dan's claims about it were unquestionably true. They were. The immediate point was that this code got into trouble with dumb pointers, no smart pointers required. Connecting that back to the rest of the discussion is something I'm afraid you'll have to work out for yourself, since this seems to imply that you lost track of the thread a significant but unknown ways back in the conversation. I've been reading as you two talked, and I never felt confused (I even felt I understood '''your''' comments :-) although I disagreed with some of them), so I don't know what to say other than, go re-read from the beginning and work it out to your own satisfaction. -- Doug I can only guess that the disconnect sprang from Dan's statement: ''"Wanna bet? You seem to be assuming a default copy constructor. That choice, and the choice of using a bare pointer with explicit release, determines that a copy-constructed instance of the class will share t's Thing between two instances."'' I think decisions about copy constructors and instance sharing are outside the scope of our discussion of memory leaks caused by exceptions in constructors. If he's saying that avoiding smart pointers will force us to use a default copy constructor, or that it will force us to share instances of t, or that it will prevent us from managing t's lifecycle, then I think he's demonstrably wrong. But I'm not sure what he's saying. I'm not assuming a default copy constructor. I'm not assuming that copies of MyClass will share instances of t. -- EH My point throughout has been consistent: Your suggestion that smart pointers should be avoided is bad advice, based on specious arguments. The discussion is not only about memory leaks in constructors; that was another page. To support your general claim about smart pointers while examining ''only'' constructors would be ridiculous. As Doug suggested, re-read ''this'' page from the beginning. In addressing your individual vague objections, I've had to guess at assumptions that you left unspecified; I tried to be clear when I did so. If you want to continue and clarify the discussion, give some coherent argument as to why inline code is preferable to smart pointers, with more detail than you have given so far. If you work out the details yourself, you may find that your concerns evaporate without further input from others. -- DanM Inline code is preferable to smart pointers (in the example given on MemoryLeakInCpp) because it decreases coupling. The use of smart pointers for member variables introduces a dependency between the smart pointer implementation choice and external uses of the containing class. In the absence of smart pointers the class can be written so that there are no such dependencies. No matter what someone does with an instance of the class, its internal implementation won't have to be changed. Which part of this do you find specious? -- EH * Use PimplIdiom, then. HerbSutter's book ''More Exceptional C++'' has an excellent section on why, even with SmartPointers (specifically auto_ptr, which is unlikely to be changed and thus introduce problems), use of pimpl is frequently necessary to provide strong exception-safety for classes. (Without smart pointers, forget it). -- ScottJohnson * ''PimplIdiom doesn't reduce the coupling I'm talking about. -- EH'' * Sure it does. The use of PimplIdiom I'm referring to is putting the smart pointers inside the implementation object; in that way, clients won't be dependent on the implementation. Worrying about a dependency on auto_ptr (part of the C++ standard) itself is silly - should we avoid memory allocation altogether because that introduces a dependency on ? -- ScottJohnson All of it, but most particularly "In the absence ... no such dependencies". Here's what I suggest you do, because I don't have time to do it for you, nor do I know what details would satisfy you. Write some examples of your hypothetical external interactions with objects of MyClass that supposedly influence your choice of pointer. Write this first, so you have to think about the external interface of MyClass that you want. Then try writing two implementations of MyClass, one using inline code, the other using auto_ptr. Compare them. If you find that there is more coupling one way or the other, show us the examples and explain why you think so. I expect that you will find the two implementations to be trivially different in behavior and code, with the smart pointer implementation being slightly more compact and readable. Those latter two points will become more important as one moves beyond trivial and incomplete examples towards real code. You're being no more concrete than the first time around. -- DanM As I said above, "copying an object containing a std::auto_ptr nullifies the pointer", so I wouldn't use auto_ptr for one of my implementations. It sounds like you don't believe that a C++ class containing a member on the heap can be written in such a way that its internal implementation doesn't depend on its external use. I'll prove that wrong the next time I have access to a C++ compiler. -- EH I couldn't possibly address that, because you've been consistently vague about what external uses you're talking about, and you haven't given example code that illustrates what you're talking about. I already showed above how a copy constructor might look with bare versus auto pointers, and the differences are trivial-to-non-existent - as are the consequences for most "external uses" that I can easily think of. Hope you find a compiler soon. -- DanM * If you use auto_ptr in an object, you should '''always''' provide a non-default CopyConstructor and AssignmentOperator; or else disable these (by making them private, etc). Generally, in such cases, you will want to perform a "deep copy" (which requires a non-default copy ctr regardless of whether smart pointers are used) - auto_ptr is appropriate for deep copies. (Or use a smart pointer which deep-copies its referent when copied). shared_ptr is appropriate for objects which are shallow-copied. -- ScottJohnson ---- ''With just a little bit of imagination and some non-trivial sweat you could make smart pointers that have no problems with cycles. However, C++ is no longer my problem for a while now :) On a second thought, it '''would''' become my problem if I wanted to port my WcpEssExpressions tools to make them interoperable with C/C++. On the other hand C/C++ developers seem content with Xerces et. comp. -- Costin'' * I've done that, but the complexity and overhead typically make an alternate approach desirable (such as you switching to Java, yes, I got that. ;-) -- Doug ---- Combining auto_ptr with a weak reference object produces a pretty safe approach to smart pointers. The auto_ptr ensures safe destruction, and the weak reference means that the program will fail gracefully if the object is destroyed later. Simply have the weak reference throw a nullptrexception on dereference if the root object is gone. It is possible to create cycles with auto_ptr, but very hard to do accidentally. This way, you get a solid pointer system with none of the ambiguity of a garbage collector, nor the busiwork of manual management. There is still some mental work of keeping track of "who owns who" but I find that usually this is pretty obvious. The only ultimate "don't worry about it" pointer system is a Java-style garbage collector. The GC is unusable for real-time applications because of it's temporal ambiguity. It also makes destructors a pain. A GC in C++ might be possible, but only if there was some way to iterate accross a collection... which might be doable if every single member of a given class derived from a "gc_enabled" class, which included setting the very first member of the class to be the sizeof() of the type. You could then iterate across the blob of memory for each object, casting to "gc_enabled" to perform operations then using the sizeof information to figure out how to move on to the next. --MartinZarate ''It is possible, and it already exists. See GarbageCollectionInCpp. -- MichaelSparks'' ---- Partly in summary, and partly based on original experience - some advice on smart pointers: * Know WHY you are using them; select an implementation which is appropriate. Know what the effect on your code will be. * RollYourOwn smart pointers are fraught with peril; unless you have a '''good''' reason for doing your own (and an '''expert''' C++ programmer on staff, not a ThreeStarProgrammer who sees the task as an interesting mental exercise), use a pre-existing and robust implementation. The two mentioned above are both excellent candidates. * Know the limitations: In particular, smart pointers are not suitable replacements for: ** Full tracing GarbageCollection, due to the cycle issue above. For many applications, use of an external GC (or a language other than C++) may be more appropriate. ** (insert others here) * Make sure your compiler can support them. Most modern compilers can. (One of the projects I work on, OTOH, still uses friggin' GCC 2.7.2 - the world of EmbeddedSystems is like that). * Make sure your staff has minimal understanding of templates, what the angle-brackets do. There probably are some C++ programmers out there who don't, though many of them now program in Java. :) * Even if you don't need the capabilities of a SmartPointer, a DumbedDownPointer may be a useful (and safer) alternative to an ordinary pointer. * Keep in mind that SmartPointer''''''s are intended to be ''useful'' in some circumstances. They are not a panacea. The fact that they are not a cure for all problems does not invalidate their use for those places where they are useful. * Keep in mind, as well, that it is difficult to prevent willful programmers from flaunting your nice SmartPointer abstractions with a well-placed use of unary &. For custom classes, overloading/removing operator &() may be an option (though it may cause more problems than its worth). For standard datatypes and stuff you get from third parties; a firm rule of DontDoIt may be the best option. (Likewise with assorted unholy uses of reinterpret_cast to extract the raw pointer from the smart pointer implementation; though this latter sort of hackery is seldom, if ever, excusable). ---- See SmartPointer JanuaryZeroSix ---- CategoryCpp CategoryGarbageCollection