Tuesday, July 5, 2011

Auto pointer


auto_ptr sounds some freaky kind of. But don’t think wrong, it is very useful in programming actually very useful. Now what are auto_ptr?
Auto pointer comes with STL's standard namespace. auto_ptr is a template class created for encapsulating any dynamically created object majorly using new operator. They acts as a container for dynamically created objects on the free space, once you allocate a memory block you need not really care about deallocating memory because auto pointer takes care of freeing that memory area once the object goes out of scope.

Better we take a look over an example first,
 
int someFunc(){
        T* pointer = new T;    // generic allocation of type T
        /*
         *      your code
         */ 
        delete pointer;         // lets free that memory
        return some_value;
}
 
This code works very well if it doesn't do anything unusual. What if someFunc never executes the delete statement, say because of a condition which causes the control to go back to the caller as a result of the function return or because of an exception thrown during the execution of the function. This kind of situation causes our famous enemy memory leaks.
The first question that will came is how to deal with this situations. The answer to this problem is auto pointer (auto_ptr), we can just wrap the pointer declared in the code above into a smart pointer that is auto pointer object and forget about the memory leak problems. Now even if the code execution doesn’t reach to the delete statement we can make sure that the memory pointed by pointer variable gets cleaned up when the function’s stack unwinds.

Here is the corrected someFunc declared again,

#include <iostream>
#include <memory.h>
 
using namespace std;
 
int someFunc ()
{
        T* pointer = new T;    // generic allocation of type T
        /*
         *      your code
         */
        // destructor gets called as the object goes out of scope
        return some_value;
}
In the above code we need not even have to explicitly call delete on the pointer. The auto pointer automatically frees up the memory of the contained object on destruction.
Using an auto_ptr is just the same as using built-in pointer, and to "give back" the resource we just call release():
 
class CSomeClass {
        private:
               int m_nValue;
        public:
                CSomeClass () {
                        m_nValue = 0;
                }
                CSomeClass (int nValue) {
                        m_nValue = nValue;
                }
                void m_SetValue(int nValue) {
                        m_nValue = nValue;
                }
                void m_PrintValue() {
                        printf("m_nValue: %d\n", m_nValue);
                }
                CSomeClass & operator =(int nValue) {
                        m_nValue = nValue;
                        return *this;
                }
};
 
int someFunc () {
        printf (“lets start with an object first\n”);
        CSomeClass *myptr1 = new CSomeClass ();
 
        auto_ptr myptr2( myptr1 ); // passing to an auto_ptr
 
        // use the auto_ptr same as pointer
        *myptr2 = 12;                  // same as "*myptr1 = 12;"
        myptr2->m_PrintValue();        // same as myptr1->m_PrintValue();"
 
        // get() to check the pointer value
        assert( myptr1 == myptr2.get() ); 
 
        // release() to return to original pointer
        CSomeClass * myptr3 = myptr2.release(); 
 
        // delete the object as no auto_ptr here now
        delete myptr3; 
        return some_value;
}

We can use reset() function to reset the auto_ptr to take a different object. If the auto_ptr already has an object, then first it deletes the that object.

int someFunc () {
        auto_ptr aptr( new CSomeClass (10) ); 
 
        aptr.reset( new CSomeClass (20) );
        // deletes the first CSomeClass that was
        // allocated with "new CSomeClass (10)" 
 
} // aptr goes out here
  // the second CSomeClass is also deleted
 
class CSomeClass {
        public:
                CSomeClass () {
                       m_pHisName = NULL;
                }
                ~ CSomeClass () {
                       delete[] m_pHisName;
                }
                void m_SetupHisName(int nSize) {
                       m_pHisName = new char[nSize];
                }
                void m_SetHisName(char* pHisName) {
                       strcpy(m_pHisName, pHisName);
                }
                char* m_GetHisName() {
                       return m_pHisName;
               }
        private:
                char* m_pHisName;
};
 
class CSomeEmp {
        public:
                CSomeEmp () {
                        m_ptrHisName = new CSomeClass ();
                }
                ~ CSomeEmp () {
                       delete m_ptrHisName;
                }
 
                void m_Name(char* pName) {
/*
 * Runtime Exception: allocate memory before setting
 * name by using syntax m_ptrHisName->m_SetupHisName(<size>);               
 */
                       m_ptrHisName->m_SetHisName(pName);    
                }
                void m_PrintHisName() {
                        printf("Name: %s\n",m_ptrHisName->m_GetHisName());
                }
        private:
                CSomeClass * m_ptrHisName;
};
 
void someOutsideFunction()
{
        CSomeEmp objEmp;
        objEmp.m_Name("Saurav"); // Exception Here
        objEmp.m_PrintHisName();
}

Its certain that the program execution stops whenever an exception occurs, if you notice the code carefully there is a memory leak in there as class CSomeEmp creates CSomeClass instance using operator new in its constructor. This allocated memory never gets freed up as the code execution never reaches upto this point. We can certainly make the memory allocation exception safe by encapsulating the CSomeClass pointer into an auto_ptr object, Let’s correct the CSomeEmp class now,

class CSomeEmp {
        public:
               CSomeEmp () {
                       m_ptrHisName = auto_ptr(new CSomeClass ());
               }
               ~ CSomeEmp () {
                       CSomeClass * scptr = m_ ptrHisName.release();
                       delete scptr;
 
               }
 
               void m_Name(char* pName) {
/*
 * Runtime Exception: allocate memory before setting
 * name by using syntax m_ptrHisName->m_SetupHisName(<size>);               
 */
 
                       m_ptrHisName->m_SetHisName(pName);    
               }
               void m_PrintHisName() {
                       printf("Name: %s\n",m_ptrHisName->m_GetHisName());
               }
        private:
               auto_ptr m_ptrHisName;
};
 
The code above creates a auto_ptr object member of CSomeEmp class. By this we ensure that the memory contained by the m_ptrHisName object gets frees up even in case an exception occurs.

No comments:

Post a Comment