Introduction
The solution
What are the benefits?
Advanced TAutoPtr<> methods
TAutoPtr<>
template class develops the idea of AutoPtr<>
published at CodeGuru
05-Jan-2000. You may find the corresponding article at
http://www.codeguru.com/cpp_mfc/AutoPtr.html.
That article is out-of-date now, but you may read it for better understanding the main concept.
AutoPtr<>
had a disadvantage viz its cleanup function used void*
parameter and
had to guess about the TYPE
of the resource it should deallocate. The common
problem (and solution) relative to void*
is described in the Comments section below the
mentioned article. TAutoPtr<>
uses a better approach. It uses a CLEANUPOBJECT
instead of old-styled AutoPtr
's CLEANUPFXN
.
TAutoPtr<>
is designed to be used within a block of code. It stores the
pointer to the object of type TYPE
(the first template parameter). It is assumed
that the object of type TYPE
is a dynamically allocated resource, so after using
this resource the programmer must call a corresponding deallocating procedure
(such as delete
, delete[]
, decrementing the object reference counter or
something else). The necessity of calling such deallocating procedures leads to
many additional lines of code in your project, the more lines the more branched
your algorithm is. Note also you can simply forget to deallocate a resource in
some branch of your algorithm :-(. So... better automate the resource
deallocation. TAutoPtr<>
interface emulates the behavior of plain C-style
pointer, so the syntax is pretty much familiar to you. Instead of the the
following code
{
CMyClass* pMyClass = CMyClass::CreateInstancePtr();
pMyClass->SomeMethod();
CMyClass::ReleaseInstancePtr(pMyClass);
}
you can write
{
TAutoPtr<CMyClass, SReleaseInstancePtr> autoMyClass( CMyClass::CreateInstancePtr() );
autoMyClass->SomeMethod();
}
SReleaseInstancePtr
is a class (or a struct, in my example S stands for
'struct') encapsulating the resource deallocation procedure. It must meet the
requirements of std::unary_function<>
, because TAutoPtr<>
uses its CLEANUPOBJECT
parameter
in the std::unary_function<>
manner. After leaving the scope
SReleaseInstancePtr::operator()(CMyClass*)
will be called inside TAutoPtr<>
destructor. This call should deallocate the resource. What does it mean "to
deallocate the resource" is up to the programmer. This should depend on the way
you are going to receive the pointer to the resource. In my example the
implementation is pretty simple:
struct SReleaseInstancePtr
{
void operator()(CMyClass* p)
{
if(p)
CMyClass::ReleaseInstancePtr(p);
}
}
Or even simpler, if CMyClass::ReleaseInstancePtr()
cares itself about processing
the NULL
value passed to the parameter p:
struct SReleaseInstancePtr
{
void operator()(CMyClass* p)
{
CMyClass::ReleaseInstancePtr(p);
}
}
To shorten a long TAutoPtr<>
declaration you can write a typedef
:
typedef TAutoPtr<CMyClass, SReleaseInstancePtr> TMyClassAutoPtr;
and then use this typedef
throughout your code:
TMyClassAutoPtr autoMyClass( CMyClass::CreateInstancePtr() );
The resource is released automatically, this saves programming efforts and
prevents resource leaks, especially in large progects.
The same mechanism can be used for automatic releasing heterogeneous resources
(memory, files, semaphores, COM objects etc). Just implement your special
CLEANUPOBJECT
for your concrete resource type.
Another advantage is the CLEANUPOBJECT
may be a template itself. This allows
releasing different resources with a similiar deallocation interface using just
a single template:
template<typename TYPE>
struct SReleaseInstancePtr
{
void operator()(TYPE* p)
{
TYPE::ReleaseInstancePtr(p);
}
};
In some cases it is necessary to explicitly "forget" to release the resource.
Use the Forget()
function for this. After calling Forget()
the resource will NOT
be destroyed after leaving the scope.
{
TAutoPtr<CMyClass, SReleaseInstancePtr> autoMyClass( CMyClass::CreateInstancePtr() );
autoMyClass->SomeMethod();
autoMyClass.Forget();
}
The TAutoPtr<TYPE, CLEANUPOBJECT>::m_bOwns
member variable indicates whether
TAutoPtr<>
must deallocate the assigned resource inside the destructor.
Some functions (such as ByRef()
, ByPtr()
, Clear()
)
use an additional parameter to manipulate this flag explicitly. This is rarely necessary, the
default behavior is consistent, but you may try playing this flag for a very tricky coding.
Some function may return the resource pointer via a C++ reference to pointer or
a pointer to pointer. To handle this use ByRef()
or ByPtr()
methods
respectively. For example, the resource allocation function may have the following signature:
static bool CMyClass::CreateInstancePtr(CMyClass*& rpMyClass);
Suppose it returns true
if the resource is allocated successfully.
You can write the following code using TAutoPtr<>
. Note that this code is safe
(from the viewpoint of memory management) independing on whether the resource
was allocated successfully or not:
{
TAutoPtr<CMyClass, SReleaseInstancePtr> autoMyClass;
if( CMyClass::CreateInstancePtr( autoMyClass.ByRef() ) )
{
autoMyClass->SomeMethod();
}
}
Note also that the TAutoPtr<>
copy constructor
and the corresponding assignment
operator
are declared private. This is correct, because copying or assigning
TAutoPtr<>
instances is potentially unsafe. It is not possible to write the
TAutoPtr<TYPE, CLEANUPOBJECT>::operator=(const TAutoPtr<TYPE, CLEANUPOBJECT>&)
suitable for all possible situations. There are pitfalls. Better avoid them.
For other details I advise you to explore the TAutoPtr<>
source code
and debug the sample application. Both are tiny.