In addition to the handle-body and singleton patterns, the Smart Pointer package contains three lower-level smart pointer classes that you can use to create your own higher-level abstractions. These template classes all provide exception-safe dynamic memory allocation. In addition:
RWTOnlyPointer<Body> implements strict ownership semantics
RWTCountedPointer<Body> automatically garbage-collects reference-counted objects.
RWTCountingPointer<Body,Counter> uses reference counting to garbage-collect objects that do not directly support reference counting.
Each of these classes is an abstraction that simplifies the use of pointers referring to objects on the heap. The pointed-to object is called the pointer's body. The pointer class's Body template parameter specifies the type of object to which this pointer can refer.
RWTOnlyPointer is useful for reclaiming dynamically allocated memory, especially when the block of code in which the memory is allocated may exit prematurely due to an exception being thrown. By wrapping a raw pointer in an RWTOnlyPointer object, you can depend on the smart pointer's destructor to delete the raw pointer when the exception causes the stack to unwind. This is an example of the "resource acquisition is initialization" technique described by Stroustrup in The C++ Programming Language (see the bibliography).
This class implements strict ownership semantics, so that only one RWTOnlyPointer can own a particular body at a given time. If the assignment operator or the copy constructor is called, then the right-hand instance is invalidated (its body pointer is set to NULL), and the left-hand instance assumes responsibility for deleting the body. Thus RWTOnlyPointer is best for local protection of dynamic memory; it is not suitable for sharing pointers among several objects. Sharing pointers can be accomplished with RWTCountingPointer and RWTCountedPointer, as described in Section 7.5.2 and Section 7.5.3, respectively.
To see why RWTOnlyPointer is useful, consider Example 64.
Class A { public: A() {p_ = new int; initialize(); } ~A() {delete p_;} private: int * p_; void initialize(void); };
If initialize() throws an exception, the allocated memory is not released. You can fix the memory leak problem by using RWTOnlyPointer, as shown in Example 65.
#include <rw/pointer/RWTOnlyPointer.h> Class A { public: A() {p_ = new int; initialize(); } // 1 ~A() {} private: RWTOnlyPointer<int> p_; void initialize(void); };
//1 | The RWTOnlyPointer instance member is constructed. The allocated int is deleted automatically if an exception occurs inside initialize(). |
Keep in mind that the copy construction and assignment semantics of RWTOnlyPointer modify the right-hand instance so that it doesn't point to the body anymore. So, for example:
RWTOnlyPointer<int> p1(new int(10)); //1 RWTOnlyPointer<int> p2 = p1; //2 cout << *p1; //3
RWTCountedPointer is a smart pointer template class that acts as a handle to a reference-counting body. Each time an instance of this class is bound to a body instance, it increments the reference-count maintained by that body. Each time an instance of this class detaches from a body instance, it decrements the body's reference-count and, if there are no more references, deletes the body instance. The reference-counting relieves clients of the burden of tracking whether it is safe to delete a body instance.
The class specified as the Body parameter to RWTCountedPointer must provide:
A member function for incrementing the reference-count, declared as void addReference()
A member function for decrementing the reference-count, declared as unsigned removeReference()
The Body has the responsibility to maintain the reference-count, usually by containing an attribute member of type unsigned.
An easy way to provide these requirements is to derive the body class from the RWTCountingBody helper class. RWTCountingBody expects a mutex type as its template parameter. The mutex ensures that the reference counting is thread-safe.
Example 67 shows the use of RWTCountedPointer with a template parameter of class Foo, which is derived from RWTCountingBody. See examples\thr0200osfam\pointer\RWTCountedPointerEx.cpp for the full example.
class Foo : public RWTCountingBody<RWMutexLock> //1 { public: Foo() { cout << "I'm a foo instance. My address is " << this << endl; } ~Foo() { cout << "I'm dying ! My address is " << this << endl; } void bar(void) { cout << "I like this place: " << this << endl; } }; typedef RWTCountedPointer<Foo> FooPointer; int main() { try { FooPointer p1(new Foo); //2 { // begin new scope FooPointer p2 = p1; //3 p2->bar(); //4 } // end new scope //5
//1 | Foo inherits from the RWTCountingBody<RWMutexLock> helper class to gain multithread-safe reference-counting. |
//2 | p1 points to an unnamed Foo instance on the heap. |
//3 | In a new scope, p2 is created to point to the same unnamed Foo instance as p1. This copy construction increments the reference-counter maintained inside Foo. Figure 38 shows the pointers at this stage in the processing.
Figure 38 -- Creating a pointer by copy construction |
//4 | bar() is called for the unnamed instance stored in the heap. The displayed address is the same as when p1 was created. |
//5 | When p2 goes out of scope and is destroyed, the reference-counter inside Foo is decremented. |
Now another Foo instance and two new pointers are created.
FooPointer p3(new Foo); //6 FooPointer p4 = p3; //7 p4->bar();
//6 | p3 points to a new Foo instance on the heap. |
//7 | p4 is created to point to the same Foo instance as p3. Figure 39 shows the pointers at this stage in the processing. Notice that p2 no longer exists.
Figure 39 -- Pointers during the swapWith operation |
The swapWith() operation causes two of the pointers to trade bodies.
p4.swapWith(p1); //8 p4->bar(); } catch(RWxmsg& msg) { cout << msg.why() << endl; } return 0; }
//8 | After the swapWith() operation, p4 points to the Foo originally identified by p1, and p1 points to the Foo originally identified by p3. Figure 40 shows the pointers now.
Figure 40 -- Pointers after the swapWith operation |
RWTCountedPointer is type-intrusive; your class must implement the reference-counter or inherit from RWTCountingBody. The advantage is that by implementing the reference counter you gain efficiency and have more control over the counter's behavior. If you need a class that is equivalent to RWTCountedPointer but is not type-intrusive, you can use the RWTCountingPointer class.
The RWTCountingPointer<Body,Counter> class is similar to RWTCountedPointer<Body>, except that it doesn't require the body to implement the reference-counter. Because RWTCountingPointer supplies its own reference counting, it can garbage-collect objects that do not directly support reference counting. In particular, unlike RWTCountedPointer, RWTCountingPointer supports primitive data types as bodies.
The reference-counter for RWTCountingPointer is provided by its Counter template parameter. Both the RWSafeCounter and RWUnsafeCounter helper classes can be used as the Counter template parameter. The default is RWSafeCounter, if your compiler supports default template arguments.
You can implement exception-safe aggregation relationships using the RWTCountingPointer template class. When compared to implementations using raw pointers, this technique offers the following advantages:
Simplified assignment, copy construction and destruction implementations. In most cases, the default versions of these operations are sufficient.
Exception-safe resource allocation. Allocated memory is always released, even when an exception is thrown.
Consider the UML class diagram in Figure 41, which shows a form of aggregation called composition. Copies of the same aggregate share one instance of its part.
This form of aggregation is effective when the shared part does not change, or when you need to keep changes to all instances of the aggregate in sync. It does not suit every situation. Keep in mind that if one instance of an aggregate changes a shared part, the change affects all the instances.
An implementation of the aggregation relationship idiom is shown in Example 68. For comparison, the aggregation relationship between A and B is implemented with RWTCountingPointer, while the aggregation relationship between B and C uses a raw pointer. For the complete example, see examples\thr0200osfam\pointer\RWTCountingPointerEx.cpp.
class B { ... }; class A { public: A() : myB_(new B) { ...} //1 ... private: RWTCountingPointer<B, RWSafeCounter> myB_; }; class C { public: C() : myB_(new B) {...} C(const C& other) : myB_(new B(other.(*myB_) )) {;} //2 ~C() { delete myB_;} //2 C & operator=(const C & other) { //2 if(this == &other) return *this; delete myB_; myB_ = new B(other.(*myB_)); return *this; } ... private: B * myB_; }; int main() { A a1; A a2 = a1; //3 C c1; C c2 = c1; //4 return 0; ... }
The RWTCounter class is used as the Counter template parameter for RWTCountingPointer<Body,Counter>. RWTCounter is a concrete type derived from the abstract RWTCountingBody. Two predefined typedefs for RWTCounter are available:
RWSafeCounter, for multithread-safe applications. It uses an RWMutexLock as the Mutex template type.
RWTCountingPointer<int, RWSafeCounter> pointer = new int(10);
RWUnsafeCounter, for applications that don't require multithread safety.
RWTCountingPointer<int, RWUnsafeCounter> pointer = new int(10);
©Copyright 2000, Rogue Wave Software, Inc.
Contact Rogue Wave about documentation or support issues.