GCO4020/CSC428 - Advanced Object Oriented Techniques In C++
Week 4
The reason for this separation of interface¤ (public functions) and implementation¤ (private data and functions) is simple: if at some future time the implementation¤ has to be changed (either due to a mistake or omission in the original design, or to improve efficiency, or to provide some new functionality), any code which uses the class will be isolated from the effects of the changes.
For example, if we suddenly realized that we could not store the
guarded pointers directly in RefCount objects, but needed to store
them in some form of database, to which each RefCount stored a
pointer, then we could simply replace the RefCount::myPtr data
member with a new member (perhaps RefCount::myDatabase) and
rewrite the body of RefCount::Ptr() to do the appropriate search
and retrieval through the database pointer. So long as the signature (that is,
the name, argument list and return type) of RefCount::Ptr() was
not altered, all the code using that member function would continue to
work without alteration, regardless of the internal changes made to class
RefCount.
On the other hand, if we had originally made RefCount::myPtr
publicly accessible, then we would have to find every place in all our
code where that data member is referred to, and replace each such reference
with the complete code needed to do the database lookup.
(End of solution)
RefCount::Clone() is sufficient
to handle the char* case because char*s are
almost never used to point to dynamically allocated
single characters. However, pointers to user-defined
object types are generally almost equally likely to
be used to point at single objects or at arrays of them,
meaning that the user will sometimes have to choose
whether (and how) to specialize
RefCount::Clone() for their newly defined class
type.
RefCount::Clone(), since both are required.
RefCountSingle and RefCountArray. The "Single"
version would simply provide a standard Clone() member
based on the corresponding copy constructor, whilst the
Clone() member for the "Array" version would provide some
form of iterated copy, which could be specialized
as necessary.
RefCountedPtrTo class could still be provided.
The only difference would be that, instead of being
templated on a single type (that is, the underlying
pointer type), this version of RefCountedPtrTo
would take two type parameters: the underlying
pointer type and the type of reference counter to
use. For example:
class MyType { // ETC. };
template <> RefCountArray<MyType>::Clone(void) { // IF NEEDED }
typedef RefCountedPtrTo<MyType, RefCountSingle<MyType> > MySinglePtr; typedef RefCountedPtrTo<MyType, RefCountArray<MyType> > MyArrayPtr;
MySinglePtr myInstance = new MyType; MyArrayPtr myInstances = new MyType[100];
(End of solution)
RefCountedPtrTo dereferencing operators provide
direct access to the underlying memory being guarded.
For example it is possible to directly delete that memory
(bypassing the reference counter) by "re-referencing" the
dereferenced pointers using the "address-of" operator (&):
RefCountedPtrTo<MyType> ptr = new MyType;
delete &(*ptr); // OR: delete &(ptr[0]);
ptr went out of scope, resulting in
the memory being "re-deleted" and the free store almost
certainly corrupted.
(End of solution)
RefCount object is to track changes in
the number of references to the dynamically-allocated memory it
guards. Since it is impossible for a RefCount object to track these
changes if it is itself unchangable, it follows that, to perform it's
function, a RefCount object can never usefully be declared const.
(End of solution)
RefCountedPtrTo::operator= did
not check for assignment-to-self, when a RefCountedPtrTo was
assigned to itself, it would decrement its reference count:
myRefCount->Decr();
and then immediately increment it again (through p):
myRefCount = p.myRefCount->Incr();
In the (reasonably common) situation where the pointer's reference count was initially 1, this sequence would first decrement it to zero, causing the counter and it's guarded memory to be reclaimed. The subsequent increment (on a non-existent reference counter) would be undefined (possibly fatally so), as would all future operations through the counter.
Last updated: Fri Feb 18 11:17:29 2000