Click here to Skip to main content
Click here to Skip to main content
Go to top

A Guard class for any object/resource type

, 15 Apr 2007
Rate this:
Please Sign up or sign in to vote.
An elegant way to wrap all types that need a cleanup, to minimize the chances of memory/resource leaks.

Introduction

During the run, programs create/allocate different kinds of objects/resources. And, most of them require some sort of cleanup. For example, if you allocate a memory/object via the new operator, then you have to delete it later; opening a file via CreateFile requires you to CloseHandle; sockets require closesocket, GDI objects require DeleteObject, and so on. Hi-level languages do this automatically, whereas C/C++ programmers have to do this themselves, this is the price of flexibility.

Once some function return, you a pointer/handle to a valid object of some kind - you must destroy it later by appropriate means. That is, you have to track the lifetime of such an object. Any mistake - oops, we have a memory/resource leak. Sometimes, you need to create an object for a short duration, say for some function. Then, you need to free it before the function returns. When such a function has multiple return statements - the code becomes ugly: either you put the cleanup statement before every return, or rewrite the function to have a single return statement. You can also use a SEH mechanism: put all the cleanups in the __finally block. In fact, that is what you must do if you work with exception handling: upon exception, you'll not reach the return statement.

If you do the work in plain C, then you seem to have no choice, but there's a more elegant way in C++. One of the most valuable and important features of C++ is destructors. That is, the compiler is responsible to track the lifetime of C++ objects and call their destructors when necessary. If you declare an object in a scope/function, then its lifetime is limited to this scope/function, and C++ guarantees that upon leaving it, the destructor will be called. All you have to do is place the pointer/handle inside some object that you declare in the scope, and free it in the destructor. Make the compiler work instead of you.

Well, I'm not the first who thought about this, of course; this is a well-known technique. I've been using and enjoying it for a long time. But recently, I've discovered an even more elegant way to use it.

Suppose we want to create a class that wraps BSTRs. The cleanup function is SysFreeString. So, we declare a class, say Bstr_G (the G suffix means 'guarded'), for it. Now, what do we want to have in this class ?

  1. Empty constructor, that sets the encapsulated BSTR to NULL.
  2. Constructor that receives the BSTR and saves it.
  3. Destructor (most important) that destroys the BSTR if it is non-NULL.
  4. A cast to type BSTR.
  5. Explicit Destroy. If the BSTR is non-NULL - free it and set to NULL.
  6. Attach. Destroy the previously held BSTR (if valid) and save the new one.
  7. Detach. Return the BSTR and set it to NULL without destroying it.
  8. Assignment operator, similar to Attach.
  9. private/protected copy constructor and assignment operator. There should not be more than one object that wraps the same BSTR, hence it is good to disable them.

Not too complex, a bit of routine work. Suppose, now, that you need a class that wraps an HICON. You declare a class, say HIcon_G, that has similar functionality, and write all the things again, except you put HICON instead of BSTR, and DestroyIcon instead of SysFreeString. In such a way, you have to do a lot of routine work to prepare such classes for different types.

Rewriting the same thing multiple times is not a good practice. It's very annoying, you can make a mistake during the rewrite. Plus, if you find a bug eventually (or want to add some extra functionality), you have to fix it in all the classes. Hence, I decided to use a more elegant way: template classes. That's exactly what they are for.

That is, instead of writing the guard classes for different types, we'll write a single template class that takes the following parameters:

  1. The type we're encapsulating.
  2. The 'invalid' value for this type (usually NULL).
  3. The cleanup function.

Then, just instantiate the specialized template version for all the types you want.

Implementation

There's no need to explain the code in-depth. It's not too complex, you can figure it out by yourself. Let me just point out some important things:

There's a base class GBase_T, which shouldn't be used as is. Instead - there're two classes inherited from it: GObj_T and GRef_T. The GObj_T class encapsulates simple types like those in our example, whereas GRef_T encapsulates types that support referencing. In particular, when you save some value, it demands to call an additional function; for such types, copy constructors can be enabled, the meaning of assignment and attaching is different, and etc. It can be used to implement smart pointers or something similar.

Next, you can pass a type and a value as a template parameter, but you can't pass a function (that is needed for cleanup). Hence, in order to use those template classes, you should pass another class that has the needed function declared, that's how it's implemented. That class that you pass must provide the following things:

  1. A typedef statement that defines the GuardType.
  2. The value itself saved in the m_Value member.
  3. An invalid value for this type, through a static function GetNullValue.
  4. Optionally, a reference method, which is called if you use it with GRef_T.

Providing such a class can also be simplified in most of the cases. There're some different template base classes for this purpose (GBaseH_XXX).

Usage example

Let's give a couple of examples about how to use it exactly.

For example, you want an HICON guard that is created via CreateIcon (don't use it for icons loaded via LoadIcon, they don't need cleanup).

// Declare a helper class that implements the 4 things GObj_T needs

struct GBaseH_DestroyIcon : public GBaseH_CoreNull<HICON>
{
    // the class we've inherited implements 3 things:

    // 1. Defines the HICON to be the 'guard' type

    // 2. Declares a variable m_Value of type HICON

    // 3. Defines NULL as invalid value.

    // The only thing that is left is the cleanup function. Declare it:

    void Destroy() { VERIFY(DestroyIcon(m_Value)); }
};
// Instantiate the specialized version of GObj_T, call it HIcon_G.

typedef GObj_T<GBaseH_DestroyIcon> HIcon_G;


    // In some function

    HIcon_G hIcon = CreateIcon( ... );
    if (hIcon)
    {
        // ...

        return;
    }
    // ...

    if ( ... )
        return;
    // ...

    return;

File handle encapsulation example is shown next. Pay attention: the 'invalid' value for file handles is not NULL, it's INVALID_HANDLE_VALUE. Then, you may declare it this way:

struct GBaseH_FileClose : public GBaseH_Core<HANDLE>
{
    static HANDLE GetNullValue() { return INVALID_HANDLE_VALUE; }
    void Destroy() { VERIFY(CloseHandle(m_Value)); }
};
typedef GObj_T<GBaseH_FileClose> HFile_G;

    // In some function

    HFile_G hFile = CreateFile( ... );
    if (hFile)
    {
        // ...

        WriteFile(hFile, ... );
        // ...

    }
    // ...

Attention: When you use the hFile in the expression that expects a HANDLE (WriteFile) - our object returns the wrapped handle, but when you use hFile in a conditional statement (if) - then our object returns if it differs from INVALID_HANDLE_VALUE. For types in which invalid value equals to 0/NULL, this is the same, but in our case, this is different. If you're not certain about some expression and afraid of the mess - you can use IsValid and GetValue explicitly.

As an example of reference-able objects, we may demonstrate smart pointers:

struct GBaseH_IMyInterface : public GBaseH_CoreNull<IMyInterface>
{
    void Reference()
    {
        m_Value->AddRef();
    }
    void Destroy()
    {
        m_Value->Release();
    }
};
typedef GRef_T<GBaseH_IMyInterface> IMyInterfacePtr;

    // In some function

    IMyInterfacePtr pMyObj = ... ;

    IMyInterfacePtr pMyObj2 = pMyObj; // will add extra ref to the object

    // ...

Additional notes

If you tend to use exception handling (as I do) - you'll probably find this method very handy. Because otherwise, you'd have to place a __try - __finally block in every function that needs a cleanup.

You should, however, know that the compiler supports two exception handling models: the so-called synchronous and asynchronous. In the first model, the compiler assumes that exceptions may not occur unless you put a throw statement or call another function that may use throw, whereas the second model states that exceptions may occur everywhere. In the first case, you get a bit smaller and faster code (sometimes the compiler skips __try - __finally blocks), whereas in the second case, you are guaranteed that the destructor will be called, even if you get GPF or etc. I personally always use asynchronous exception handling, but unfortunately, the default is synchronous. To enable asynchronous exception handling, either find it in the compiler settings, or put the /EHa flag.

To get immunity against memory/resource leaks, never let your pointers/handles unguarded. For example, don't write it this way.

    PBYTE pPtr = new BYTE[nSize];
    // ...

    // If exception is raised here - the pPtr will be lost

    // ...

    delete[] pPtr;

Better to write it the following way:

    GPtr_T<BYTE> pPtr = new BYTE[nSize];

If you want your function to return a pointer, then instead of writing it this way:

PBYTE AllocMyArr()
{
    // ...

    PBYTE pPtr = new BYTE[nSize];
    // Fill the pPtr with some data

    // ...

    // If exception is raised here - the pPtr will be lost

    // ...

    return pPtr;
}

You better rewrite it in the following way:

void AllocMyArr(GPtr_T<BYTE>& pPtr)
{
    // ...

    pPtr = new BYTE[nSize];
    // Fill the pPtr with some data

    // ...

}

That is, don't leave pointers unattended.

Another important note: sometimes, it is good to override the GObj_T/GRef_T instead of just instantiating it via typedef. This allows to add some extra functionality that was not designed in the base class. For example, we may want the file handle wrapper to have Write and Read member functions for convenience.

There is a very serious pitfall here: Suppose you write something like this:

class HFile_G : public GObj_T<GBaseH_FileClose>
{
public:
    // constructors, we have to rewrite them in inherited class.

    HFile_G() {}
    HFile_G(HANDLE hFile) : GObj_T<GBaseH_FileClose>(hFile) {}
    // extra members

    void Write( ... );
    DWORD Read( ... );
};

    // In some function

    HFile_G hFile;
    // ...

    hFile = CreateFile( ... );
    // ...

    hFile = CreateFile( ... );
    // ...

Guess what this will do. You won't believe!

Do you expect that on the first CreateFile we save the returned handle, and at the second call, we close the previous handle and save the new one? Wrong!

At the inherited class, we didn't implement the assignment operator that takes a HANDLE. It exists in our base class, but according to C++ rules, it is not automatically inherited. But, this is not the worst thing: the compiler won't give you an error here. This is what it will do instead:

  1. Create a temporary HFile_G object from the handle you pass. This is possible because we have the appropriate constructor.
  2. Assign our object to this temporary one. How can this be possible if we didn't implement it? Don't worry, the compiler generates the assignment operator automatically. How? By just copying the members (!!!). So that our previously saved handle is overwritten by the new one.
  3. As we said, the HFile_G object generated by the compiler is temporary, its lifetime is the assignment statement. So, immediately after the assignment, the compiler calls its destructor. Hence, the new handle is closed.

Impressing, isn't it? Instead of closing the old handle, we'll close the new one! And you have no errors/warnings. You just get the surprise at the runtime. In my opinion, it is an extremely stupid idea to generate an assignment operator / copy constructor automatically, especially if your base class is a non-trivial one. But this is the reality.

To avoid such situations, you must implement assignment and copy constructor in the inherited class. Furthermore, you have to implement (or disable by declaring it private) three assignment operators: one that takes a HANDLE, one that takes a const reference to the base class, and one that takes a reference to your class. The same applies to constructors.

I've also pointed on this issue in the comments in the code. To make the inherited class work correctly, you can write it the following way:

class HFile_G : public GObj_T<GBaseH_FileClose>
{
    INHERIT_GUARD_OBJ(HFile_G, GObj_T<GBaseH_FileClose>, HANDLE)
    // extra members

    void Write( ... );
    DWORD Read( ... );
};

This macro will implement all the problematic functions.

This is all. Hope you'll find this useful. Comments are appreciated.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

valdok
Software Developer (Senior)
Israel Israel
My name is Vladislav Gelfer, I was born in Kiev (former Soviet Union), since 1993 I live in Israel.
In programming I'm interested mostly in low-level, OOP design, DSP and multimedia.
Besides of the programming I like physics, math, digital photography.

Comments and Discussions

 
GeneralUsing the keyword "explicit" helps avoid accidental creation of temporaries Pinmemberwtwhite19-Apr-07 18:11 
GeneralIt's a bit restrictive PinmemberVladislav Gelfer22-Apr-07 23:51 
GeneralRe: It's a bit restrictive Pinmemberwtwhite23-Apr-07 13:25 
NewsRAII PinmemberVirtual Coder16-Apr-07 23:43 
GeneralNot exactly I think PinmemberVladislav Gelfer17-Apr-07 6:30 
GeneralRe: Not exactly I think PinmemberVirtual Coder17-Apr-07 8:50 
Vladislav Gelfer wrote:
However it's not exactly "Resource Acquisition Is Initialization" if I understood it right;

 
Yes and No. Yes, RAII originally meant "Resource Acquisition Is Initialization". No, today it just means what you describe: Resource Management with destructors of automatic objects. See e.g. http://en.wikipedia.org/wiki/RAII[^] and http://www.research.att.com/%7Ebs/bs_faq2.html#finally[^].

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140922.1 | Last Updated 15 Apr 2007
Article Copyright 2007 by valdok
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid