Smart Pointers and the Undetermined Ownership Problem

As mentioned previously, the undetermined ownership problem is characterized by a module obtaining a dynamic object from some other module without also obtaining the ownership (i.e., the responsibility of deallocating it). In most cases a dynamic object is acquired through a reference - that is, a pointer. In this discussion we will call the ordinary pointers naked pointers. From the ownership point of view, a naked pointer has the following property: the default state is to "preserve untouched" the object it references when it goes out of scope, and if the object is to be deallocated then this must be done explicitly before the pointer goes out of scope. In many situations we would like to reverse things and have as the default the "destruction" of the object being referenced; then by some explicit means we could achieve " preserving " the object untouched when the pointer goes out of scope. This is the main idea behind safe or smart pointers.

Let us illustrate the concept of smart pointers for a class ACLASS , a kind of "wrapper" around the naked pointer.

 class ACLASSPtr { public:  ACLASSPtr(ACLASS *a) : naked_ptr(a) { } //init naked_ptr  ~  ACLASSPtr() { delete naked_ptr; }      //delete the object *naked_ptr  ACLASS* operator->() const {            //define ACLASSPtr->   return naked_ptr;  }  ACLASS& operator*() const {             //define *ACLASSPtr   return *naked_ptr;  } private:  ACLASS* naked_ptr; };//end class ACLASSPtr 

Let as assume that A is an object of type ACLASS and that a naked pointer p points to it. The instruction ACLASSPtr smartp(p) creates and initializes a smart pointer smartp referencing the same object as p . From now on in the program p->... and smartp->... give the same result, and so will *p and *smartp . The behavior of p and smartp are undistinguishable with one significant difference: when smartp goes out of scope, the object A will be deleted.

We could easily add the counting of objects of the class ACLASSPtr (as discussed previously) and allow deletion in the destructor only if it is the very last smart pointer pointing to A . This would provide us with an automatic system, almost a garbage collector, where objects referenced via smart pointers are destroyed when no longer referenced by any smart pointer. The least intrusive implementation uses linking of the smart pointers that reference the same object, which adds two more pointers to each smart pointer ( prev points to the previous smart pointer pointing to the same object, and next points to the next smart pointer pointing to the same object). A construction of such a smart pointer must insert it in the appropriate list of smart pointers, and its destruction must delete it from the list.

In the form just illustrated , the smart pointers would not be too helpful because there are no means of passing them across function boundaries (either up using return or down as arguments). For that we need to augment our definition of ACLASSPtr by the appropriate copy constructor and assignment. For reference-counting smart pointers, both the copy constructor and the assignment must properly update the linked list of pointers pointing to the same object.

In programs with large number of objects referenced through smart pointers, the processing and memory overhead associated with reference counting may be significant. The fundamental idea of smart pointers is of course fraught with perils . If a smart pointer is made referencing an object on the stack or a static object, then any attempt to delete that pointer when it goes out of scope will result in a crash. If a single smart pointer were to reference an object that never goes out of scope (either because it is static or is dynamic and never deleted), then no object of class ACLASS would ever be deleted through a smart pointer. Thus, rather than automatic deletion based on reference count, we more often prefer smart pointers that transfer ownership so that there is just a single owner at any given moment. Usually the ownership transfer is achieved by zeroing the transferor, so when the transferor goes out of scope there is no object to be deleted. If we had some need for the transferor still to reference the object, then we could add an extra member (the ownership flag owner ) to the definition of ACLASSPtr in order to indicate who is the owner. However, in both cases the principle of ownership transfer is the same, so the interested reader can easily modify the following code, which uses zeroing for ownership transfer, to a code that uses ownership flags.

 ACLASS* Transfer() {      //provides transfer of ownership  ACLASS* p = naked_ptr;   //by zeroing the transferor  naked_ptr = 0;  return p; } //copy constructor with the transfer of ownership //since Transfer() modifies the transferor, we cannot //have the usual (const ACLASSPtr& p) declaration of  the argument ACLASSPtr (ACLASSPtr& p) : naked_ptr(p.Transfer()) { } //assignment with the transfer of ownership //since Transfer() modifies the transferor, we cannot //have the usual (const ACLASSPtr& p) declaration of the argument ACLASSPtr& operator= (ACLASSPtr& p) {  if (naked_ptr != p.naked_ptr)    delete naked_ptr;  naked_ptr = p.Transfer();  return *this; } 

It does not matter if we use static or dynamic smart pointers - as long as these "never go out of scope" smart pointers transfer the ownership. But it still is true that, if a smart pointer that is going out of scope references a static object or an object on the stack, a crash will ensue. However, a smart pointer as a local variable going out of scope in any fashion (including via stack unwinding after an exception) will delete the object it owns and thus yield code that is far more exception-safe than if the naked pointers were used. Moreover, the transfer of ownership using zeroing prevents smart pointers from dangling.

Smart pointers as discussed here are in essence the auto_ptr<X> defined in the C++ Standard Library. There it is implemented using templates, so it can be defined for any class X .



Memory as a Programming Concept in C and C++
Memory as a Programming Concept in C and C++
ISBN: 0521520436
EAN: 2147483647
Year: 2003
Pages: 64

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net