7.5 Using the Smart Pointer Classes
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:
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.
7.5.1 Using RWTOnlyPointer
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 61.
Example 61 – Allocating memory with a potential memory leak
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 62.
Example 62 – Using a smart pointer to reclaim memory
#include <rw/pointer/RWTOnlyPointer.h>
Class A {
public:
A() {p_ = new int; initialize(); } // 1
~A() {}
private:
RWTOnlyPointer<int> p_;
void initialize(void);
};
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:
Example 63 – Mistakenly using an invalid pointer
RWTOnlyPointer<int> p1(new int(10)); //1
RWTOnlyPointer<int> p2 = p1; //2
cout << *p1; //3
7.5.2 Using RWTCountedPointer
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.
7.5.2.1 Deriving from RWTCountingBody for Reference Counting
An easy way to provide these requirements is to derive the body class from the
RWTCountingBody helper class.
Example 64 shows the use of
RWTCountedPointer with a template parameter of class
Foo, which is derived from
RWTCountingBody. See
buildspace\examples\pointer\RWTCountedPointerEx.cpp for the full example.
RWTCountingBody is templatized on a mutex type in order to implement the
RWTMonitor interface.
Example 64 – Providing reference counting for a counted pointer
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
Now another Foo instance and two new pointers are created.
FooPointer p3(new Foo); //6
FooPointer p4 = p3; //7
p4->bar();
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;
}
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.
7.5.3 Using RWTCountingPointer
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, generally an instance of
RWAtomicCounter.
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 42, 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 65. 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
-buildspace\examples\pointer\RWTCountingPointerEx.cpp.
Example 65 – Implementing an aggregation relationship
class B {
…
};
class A {
public:
A() : myB_(new B) { …} //1
…
private:
RWTCountingPointer<B, RWAtomicCounter> 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;
…
}
7.5.3.1 Providing RWTCountingPointer with a Counter
The
RWAtomicCounter class is generally used as the
Counter template parameter for
RWTCountingPointer<Body,Counter>:
RWTCountingPointer<int, RWAtomicCounter> pointer = new int(10);
For more information on implementing a custom counter class, see
RWTCountingPointer in the reference guide.