If you have
looked through the simulator code. You may have noticed class names such as
ProcessPtr, GuardPtr, ExpressionPtr, etc. Class names with the Ptr suffix are
reference counted pointers to its class name prefix. For example, ProcessPtr is
a reference counted pointer to the Process class. Many C++ books discuss
reference counter pointers. Reference count may be called smart memory, smart
pointers, handle classes, envelope classes, or referencing counting. Both
Stroustrup and Coplien mention the topic. The smart pointer class used in the
simulator is based on the handle class sketched in Stroustrup's book.
The simulator is
large enough application that we need to be concerned about memory management.
Many objects share references to the same object. But, the question is who frees
the memory. With reference counting, the answer is simple. All the classes do.
Not every pointer should be reference counted. If a class is the only class
which references an object, then don't reference count. If the pointer is
shared, consider reference counting.
The smart pointer
class used in the simulator gives "almost" pointer semantics. To
create a smart pointer for a class named Box, use the macro Ptr to class the
class.
This macro
expands into the class definition for the the class BoxPtr. The methods for the
BoxPtr class are
BoxPtr() -
construct a smart pointer. (The pointer to the object is nil in this case.)
BoxPtr(const
BoxPtr&) - The copy constructor. Copy a smart pointer and increment the
reference count.
BoxPtr(const
BoxPtr*) - Create a smart pointer from a pointer to an object and make the
reference count 1.
operator=(const
BoxPtr&) - Assign one smart pointer to another. (Do the appropriate
adjustment of the reference counts.)
operator=(const
BoxPtr*) - Use an assignment statement to create a smart pointer with reference count of one.
~BoxPtr() -
Destroy a smart pointer, decrement the reference count, and if the reference count is zero destroy
the object.
operator*() -
Dereference a smart pointer. If we have a variable p which is a BoxPtr, *p is a reference to an object
of type Box.
operator->() - Returns a
pointer to a Box. This allows you to use the arrow syntax. So, you can write something like p->draw().
use() - Returns a
pointer to type Box.
steal() - Returns
a pointer to type Box. Set the reference count to zero. You
probably will never use this method.
![]()
This is a simple
example demonstration smart pointers. It demonstrates Object destruction
when a variable goes out of scope. If Box were not smart pointered, the memory allocated
by new Box would be leaked.
#include <iostream.h>
#include <Ptr.h>
class Box {
public:
Box() { cout << "Box is constructed" <<
endl; } ~Box() { cout << "Box is destroyed" << endl; }
};
Ptr(Box); make a smart pointer for Box
int main (int, char **) { cout << "BoxPtr x;\nBoxPtr
y;\n\n";
BoxPtr x; BoxPtr y;
cout << "x = new Box;\n"; x = new Box;
cout << "\nx = " << x
<< "y = " << y << endl;
cout << "y = x;\n"; y = x; cout <<
"\nx = " << x <<
"y = " << y << endl;
return
0;
}
![]()
BoxPtr x; BoxPtr
y;
x = new Box; Box is constructed
x = Rep pointer: 0x20000
Reference count: 1
Last ref value: 0
y = Rep pointer: 0 (nil reference)
x = Rep pointer: 0x20000
Reference
count: 2
Last ref value: 0
y = Rep
pointer: 0x20000
Reference count: 2
Last
ref value: 0
Box is
destroyed
![]()
Demonstrate how
smart pointers can help clean up after an exception.
#include <iostream.h>
#include <Ptr.h> class Box {
public:
Box() { cout << "Box is constructed" << endl; } ~Box() {
cout << "Box is destroyed" << endl; } };
Ptr(Box); make a smart pointer for Box
void sub2() { cout << "Throw an exception. Box should be
destroyed\n";
throw "exception thrown from sub2()"; }
void sub1(void) { BoxPtr x;
x
= new Box;
sub2();
}
int main (int, char **) {
try { sub1(); } catch ( char *p ) {
cout << "Caught: " << p << endl; }
return 0; }
![]()
Box is
constructed
Throw an exception.
Box should be destroyed
Box is destroyed
Caught: exception thrown from sub2()