Single Ownership in C++ (intro to auto_ptr) 1

Posted by Tim Ottinger Fri, 30 Mar 2007 10:48:00 GMT

I find a lot of C++ programmers experience memory management differently than I do. I used to have the same kinds of problems that they have, but I learned an important rule: every heap object has exactly one owner, and n borrowers.

The way that I realize this is using reference types. Of course, all good C++ programmers write their functions to use the canonical “const T&” parameters whenever possible. Using a reference means that we have an actual object and not a null reference (side effect: no null checks) and we have simple dot notation (side effect: less typing), but I also use this to mean that we are borrowing the object, not owning it. Likewise, if we have to drop the ‘const’, we are still borrowing.

What about passing by pointer? I only do that if I absolutely must accept 0 (null) as a possible value. I always hate to do that. I would rather have an object outright than a pointer that I dereference. If the function is being called by a framework, I don’t get a choice. I might do something like this:
     void functionName(const Snorkle* snorkleWithNull) {
             if (0 == snorkleWithNull) { 
                  throw SomeIdiotPassedMeANull();
            const Snorkle& snorkle = snorkleWithNull;

           // ... work with snorkle, ignore snorkleWithNull ... 

     }
However sick that seems (and it does) it reverts back to the const reference case quickly and I’m left with a reference to a real object. It keeps me from forgetting to dereference when I’m in a hurry, and keeps me from casting. I have to work extra-hard to do bad things, and that’s a good thing. On the other hand, in modern TDD style, with refactoring, it can double the length of a function (hence feeling sick).

Either way, if you are a borrower, you have no right to delete the object. You can reference it for the duration of your function, or (if the intent is that you will have a short-lived object) for the duration of the object lifetime, but no ownership is given by pointer or reference. Remember that we still have a risk of the pointer being dangled, so holding a reference/pointer for longer than a method call should be considered fairly dangerous and must be done rather intentionally.

But what about transfer of ownership? That’s what the auto_ptr in <memory> is for. Yes, it’s a template. If I can’t use templates, I’ll make up something new that acts roughly the same. The auto_ptr is the one structure in C++ that undeniably says “owner”. When I receive an object via auto_ptr, I am now the owner. When I return an auto_ptr from a function, I’ve clearly given up ownership.

The recipient of the auto_ptr has to do something with the referenced object, or else it will be deleted when the auto_ptr passes out of scope. If the recipient decided to release the auto_ptr, they can keep a normal pointer or reference in whatever structure pleases them. They can make a copy and delete the original (remember the warning about dangling).

The trick is to remember that 99% of the time, you want to be a borrower, and that any class that is going to hold a borrower’s reference to an object needs an algorithmic guarantee that the referenced object will outlive the reference.

The scary part is for an object to have a large number of long-term borrowers, because there is the constant fear that one might outlive the owner. In that case, you need to hang the auto_ptr on the shelf and go get some bigger magic. You might actually need the smart pointer. I have created reference-counting pointer templates in several projects, which will cause C++ to act more like a garbage-collecting language. I know that there are 3rd-party libraries for garbage-collecting. If I find myself in a situation where there are a number of long-term borrowers, I will definitely move in one of these directions again. It is too important to leave object ownership up to chance or naming convention.

Comments

Leave a response

  1. Avatar
    Fred 28 days later:

    Plug for Boost smart pointers. When I’m on a project where I can use Boost, I never allow naked pointers.

Comments