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.
Figure 42 – Two copies of an aggregate sharing one instance of a 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 Figure 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;
}
 
//1 In this example, the aggregation is established during construction of A, which creates B on the heap. Inside A, dereferencing operations applied to myB_ can be used to access its B. Because the life-cycle of B is dependent on A, this form of aggregation is called composition.
//2 Class C plays the same role as class A, but because class C uses a raw pointer, the copy constructor, assignment and destructor implementations are mandatory. Instead of sharing the same B instance, as in class A, the copy constructor allocates another B instance.
//3 The construction of a2 doesn’t imply a new instance of B. Because of reference-counting, a1 and a2 share the same B instance.
//4 The construction of c2 implies a new instance of B.