auto_ptr and its usage

Introduction

The articles main objective is to educate the newbie's how to code a memory leak free program in c++.

Its been a long time since I am thinking of writing an article on auto_ptr's. When I started learning auto_ptr's I found many articles describing what auto_ptr's really are, what are their uses and how to implement code using them. To be very frank I could't find any article which explains auto_ptr's in depth. Finally I have decided to write an article which will serve as an extention to the articles available on the web.

To start with auto_ptr's, the very first obvious question one will ask is what are auto_ptr's?.

Auto pointers ship as a part of STL's standard namespace. auto_ptr is a template class created mainly for encapsulating any dynamically created object preferably using new operator. Auto pointer's acts as a container for dynamically created objects on the free store, 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 encapsulated object goes out of scope & probably not needed anymore.

Let's have a look at the unsafe code below:

Collapse Copy Code


void myFunction()
{
	T* ptr = new T; 

	/*... code...*/ 

	delete ptr;
}

Most people like us write code like this everyday, this code will work very well if it doesn't do anything exceptional. What if myFunction never executes the delete statement, either 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 is an ideal scenario where we can have a classic memory leak problem.

Now the very first question that will popup in your mind is how to handle such difficult situations. The answer to this problem is auto pointer, we can just wrap the pointer ptr 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 ptr variable gets cleaned up when the function’s stack unwinds.

Let’s have a look at the corrected myFunction function:

Collapse Copy Code


#include 
   
     using namespace std; void myFunction() { auto_ptr ptr( new T ); /*... code...*/ // ptr’s destructor gets called as the function’s stack unwinds here & the object // gets deleted automatically } 
   

In the above code we need not even have to explicitly call delete on the ptr’s contained 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 "take back" the resource and assume manual ownership again, we just call release():

Collapse Copy Code


class CMyClass
{
private:
	int m_nData;
public:
	CMyClass()
	{
		m_nData = 0;
	}
	CMyClass(int nData)
	{
		m_nData = nData;
	}
	void SetValue(int nData)
	{
		m_nData = nData;
	}
	void PrintValue()
	{
		printf("Value is: %d/n",m_nData);
	}
	CMyClass& operator =(int nData)
	{
		m_nData = nData;
		return *this;
	}
};

void myFunction()
{
	CMyClass* ptr1 = new CMyClass();
	// right now, we own the allocated object 

	// pass ownership to an auto_ptr
	auto_ptr ptr2( ptr1 ); 

	// use the auto_ptr the same way
	// we'd use a simple pointer
	*ptr2 = 12;		// just as writing "*ptr1 = 12;"
	ptr2->PrintValue(); 	// just as writing "ptr1->PrintValue();" 

	// use get() to see the pointer value
	assert( ptr1 == ptr2.get() ); 

	// use release() to take back ownership
	CMyClass* ptr3 = ptr2.release(); 

	// delete the object ourselves, since now
	// no auto_ptr owns it any more
	delete ptr3; 
}

We can use auto_ptr's reset() function to reset the auto_ptr to own a different object. If the auto_ptr already owned an object, though, it first deletes the already-owned object, so calling reset() is much the same as destroying the auto_ptr and creating a new one that owns the new object:

Collapse Copy Code


void myFunction()
{
	auto_ptr ptr( new CMyClass(1) ); 

	ptr.reset( new CMyClass(2) );
	// deletes the first CMyClass that was
	// allocated with "new CMyClass (1)" 

} // finally, ptr goes out of scope and
// the second CMyClass is also deleted

Now comes the main part that I discussed in the begining of this article, I have not seen any real world example describing use of auto_ptr as a class level variable.

class CName
{
public:
	CName()
	{
		m_pName = NULL;
	}
	~CName()
	{
		delete[] m_pName;
	}
	void SetupName(int nSize)
	{
		m_pName = new char[nSize];
	}
	void SetName(char* pName)
	{
		strcpy(m_pName,pName);
	}
	char* GetName()
	{
		return m_pName;
	}
private:
	char* m_pName;
};

class CEmployee
{
public:
	CEmployee()
	{
		m_ptrName = new CName();
	}
	~CEmployee()
	{
		delete m_ptrName;
	}
	void Name(char* pName)
	{
		m_ptrName->SetName(pName);	// Runtime Exception: memory should have been 
						// allocated before setting name by using syntax
						// m_ptrName->SetupName(
   
    ); } void PrintName() { printf("Name: %s/n",m_ptrName->GetName()); } private: CName* m_ptrName; }; void myFunction() { CEmployee objEmp; objEmp.Name("Neeraj"); // Exception objEmp.PrintName(); } 
   

The code above will definitely cause a runtime exception as when setting employees name memory is not allocated by calling SetupName function. Its certain that the program execution stops whenever an exception occurs, if you notice the code carefully there is a classic memory leak in there as class CEmployee creates CName’s 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 CName's ptr into an auto_ptr object, following code ensures depicts the correct code of CEmployee class.

Collapse Copy Code


class CEmployee
{
public:
	CEmployee()
	{
		m_ptrName = auto_ptr(new CName());
	}
	~CEmployee()
	{
		CName* ptr = m_ptrName.release();
		delete ptr;
	}
	void Name(char* pName)
	{
		m_ptrName->SetName(pName);	// Runtime Exception: memory should have been 
						// allocated before setting name by using syntax
						// m_ptrName->SetupName(
   
    ); } void PrintName() { printf("Name: %s/n",m_ptrName->GetName()); } private: auto_ptr m_ptrName; }; 
   

The code above creates a auto_ptr object member of CEmployeeName class. By doing this we are ensuring that the memory contained by the m_ptrName object gets freed up even in case an exception occurs. Look carefully at the constructor & destructor, the class's constructor creates an instance of CName class and passes on the ownership to the class's member CName auto_ptr m_ptrName. In the destructor we have taken back the ownership of auto_ptr's contained CName class's pointer which we have deleted safely by calling delete.

When one auto_ptr's ownership is passed from one auto_ptr to another. If the lvalue auto_ptr object already contains a valid ptr then the auto_ptr first releases the memory it holds and then attaches the rvalue ptr to it.

License

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

About the Author

Neeraj S

Member
Working in C,C++ for last 5+ years. Presently working as Senior System Analyst at Infospectrum India ltd. My area of work has been designing robust Network Applications for corporate networks.

Occupation:
Systems Engineer

Company:
Infospectrum India ltd.

Location:
India India

你可能感兴趣的:(exception,object,delete,Class,Constructor,destructor)