Overview of
safe pointers
Each of these template classes is a wrapper for a pointer. Instances of safe pointers may be used wherever you might use a pointer (for example, instead of AType*, you write TInstanceOf<AType>). Like a raw pointer, a safe pointer can be dereferenced using operator * or operator ->. A safe pointer can have the value NIL (0), however, dereferencing a NIL safe pointer throws an exception. Dereferencing a NIL raw pointer, on the other hand, usually results in an attempt to interpret the memory at address 0 as an object, with unpredictable results
Each concrete safe pointer class implements an ownership policy--a convention for what code is responsible for destroying the object to which it points (and returning the object's storage to the heap).
TAliasTo<> -- The pointer does not own the object.
TInstanceOf<> -- The pointer owns the object, permanently.
TOnlyPointerTo<> -- The pointer owns the object, but may transfer ownership to another pointer.
TDeleterFor<> -- The pointer owns the object, but may transfer ownership to another pointer. TDeleterFor is very similar to TOnlyPointerTo in both syntax and semantics. TDeleterFor is actually an older implementation of a simple safe pointer. You will find it used more frequently in CommonPoint applications. However eventually use of TDeleterFor is expected to give way to TOnlyPointerTo
TDeleterForArrayOf<> -- The pointer owns the object, but may transfer ownership to another pointer.
By using safe pointers, you can clearly declare the storage management paradigm for a particular pointer, and exploit automatic object destruction (when exiting a scope, or destroying a containing object) to handle storage management or reference counting.
Safe pointers are readily convertible to and from raw pointers, although the conversion to raw pointers must be explicit. Safe pointers are implemented using non-virtual functions, and all code is in-line. In-line function definitions make safe pointers as efficient as raw pointers (at run time), except the dereferencing operators (* and ->) take more code and time (to check for NIL) than dereferencing a raw pointer.
Safe pointers are not thread-safe. Concurrent use of a wrapper object by several threads is an error; the results are not defined.
It's helpful to see how TDeleterFor is used in context. One of the benefits of using a TDeleterFor pointer is that it automates cleanup of referenced objects if an exception occurs before the pointer goes out of scope. In the following example from the StockBrowser example program a TDeleterFor is used to reference a subset of stock entries copied from the persistent DiskDictionary which holds all stock trading records.
Using safe pointers
Before going into the details of interface protocols for safe pointer classes, you may find it helpful to study examples using safe pointers in this section.
Now diskCollection is a smart pointer which can use the same syntax as if it had been declared as a normal C++ pointer to a TCollectionOf<TStockDay> object. A TStockDay is a data entry for a single day's trading information for a given company. The diskCollection smart pointer currently refers to a collection of such entries for a single company. Semantically, smart pointers are essentially the same as normal pointers, but smart pointers do extra house keeping and memory management. The intent of the function from which this code is extracted is to iterate over the collection of stock day entries for a company and select entries that fall within a specified range of dates. If an exception occurs, or program control reaches the end of the block in which diskCollection is declared, code in TDeleterFor is invoked automatically to destroy the collection referred to by diskCollection. TDeleterFor<TCollectionOf<TStockDay> > diskCollection =
fDiskDictionary->Copy(stockName);
Another common use of TDeleterFor smart pointers is with iterators. In the same member function declaring diskCollection, an iterator is used to walk through each stock day entry and select it into a target collection if the entry falls within the desired range of dates. The iterator is created as follows:
TDeleterFor<TIteratorOver<TStockDay> > iterator = diskCollection->CreateIterator();
bool TStockServer::CopyStockData( const TStandardText& stockName, TCollectionOf<TStockDay>& collectionToFill, const TRangeOfDays& rangeOfDays) { bool success = false; TDeleterFor<TCollectionOf<TStockDay> > diskCollection = fDiskDictionary->Copy(stockName); if (diskCollection != NIL) { TDeleterFor<TIteratorOver<TStockDay> > iterator = diskCollection->CreateIterator(); for (TStockDay* item = iterator->First(); item != NIL; item = iterator->Next()) { if (rangeOfDays.Contains(item->GetDate())) collectionToFill.Add(item); } success = collectionToFill.Count() > 0; } return success; }
The previous example is a case in which ownership of the referenced object is retained throughout the life of the smart pointer. Sometimes, however, smart pointers adopt or orphan referenced objects. The main benefit of using a smart pointer to create an object and then orphan it (give up the referenced object to another pointer) is to assure cleanup in the event of an exception.
The following example shows how a graphic object used in the display of the StockBrowser is created using a TGraphicGroup. A smart pointer is declared to point to a newly created group.
TDeleterFor<TGraphicGroup> group = new TGraphicGroup;
TDeleterFor<TGrafBundle> bundle = new TGrafBundle(*TColorPaint::GetWhite().GetColor());
bundle->AdoptFramePen(new TSolidPen(kWidth, TPen::kInsetFrame)); MGraphic* graphic1 = new TArea(bounds, bundle.OrphanObject()); group->AdoptLast(graphic1);
MGraphic* TViewerContentView::CreateDropFeedback(const TModelSelection& selection) const { static const GCoordinate kWidth = 1; TDeleterFor<TGraphicGroup> group = new TGraphicGroup; if (selection.IsDefined()) { TGRect nameBounds(fNameDisplay.GetTextBounds()); nameBounds.Inset(TGPoint(1.0, 1.0)); TGArea bounds(nameBounds); TDeleterFor<TGrafBundle> bundle = new TGrafBundle(*TColorPaint::GetWhite().GetColor()); bundle->AdoptFramePen(new TSolidPen(kWidth, TPen::kInsetFrame)); MGraphic* graphic1 = new TArea(bounds, bundle.OrphanObject()); group->AdoptLast(graphic1); bundle = new TGrafBundle(*TColorPaint::GetBlack().GetColor()); bundle->AdoptFramePen(new TSolidPen(kWidth, TPen::kOutsetFrame)); MGraphic* graphic2 = new TArea(bounds, bundle.OrphanObject()); group->AdoptLast(graphic2); } return group.OrphanObject(); }
In some cases, you don't need smart pointers. Notice, for example, that both graphic1 and graphc2 are declared as normal C++ pointers to TArea objects.
MGraphic* graphic1 = new TArea(bounds, bundle.OrphanObject()); ... MGraphic* graphic2 = new TArea(bounds, bundle.OrphanObject());