Memory Leaks

Memory Leaks

Arrays always allocate memory statically, i.e. during compilation. As a result the desired generality may not be obtained in the program that uses an array. To avoid this it is common practice in software engineering to dynamically allocate memory from the heap for use by a program. In C and C++ once allocated it is not binding on part of the program to release this memory when it is no longer needed. When a program fails to do so, i.e. it does not return the allocated memory to the operating system, the memory chunk remains allocated but we are unable to access it. Losing memory in this manner is often known as a memory leak. In this article I would highlight the common causes of memory leakages while programming in C++. The discussion is independent of the operating system.

Cause no.1: Not using a delete at all:

Often one forgets to use the delete operator on memory that one has dynamically allocated with the new operator. However, once you grow knee high in C++ you would almost always avoid such memory leaks.

Cause no.2: Multiple exits in a function

Ideally there should be only one exit point in a function. It is however easier to preach this than practice it diligently. If you are using multiple returns in a function it might lead to an exit from the function before dynamically-allocated memory is freed. Here is a program to illustrate the point:

int func ( node *p )
{
if ( p == NULL )
return ( false ) ;

node *ptrtonode = new node ;

if ( ptrtonode == NULL )
return ( false ) ;

switch ( ptrtonode -> performance() )
{
case FIRST :
promote ( 1 ) ;
return ( true ) ;
case SECOND :
promote ( 2 ) ;
return ( true ) ;
default:
break;
}

delete ptrtonode ;

return ( false ) ;
}

As can be observed from the above code fragment, we have possibly saved a few clock cycles by not using a local Boolean variable to keep track of success or failure. We haven't used a local variable because whenever our code encountered an error it just returned to the calling function. However, if the performance is FIRST or SECOND the memory that portioned points to never gets deleted.

Cause No.3: Using several pointers with the same name

It's common knowledge that C++ allows and encourages a programmer to declare an automatic variable close to the point of usage. Sometimes this principle might land you into trouble. Using this rule we may declare an automatic variable that will point to a quantity of allocated memory right at the place of allocation. Suppose we have declared a pointer at the start of a function (in the traditional manner) and another pointer with the same name within an inner scope, at the point of instantiation. This is likely to cause a memory leak. Here is an example program:

void change shape ( int shape )
{
node *ptrtonode = NULL ;
if ( is valid ( shape ) != false )
{
node *ptrtonode = new node ;
ptrtonode->newshape ( shape ) ;
}
// which ptrtonode is this?
delete ptrtonode ;
}

In this code fragment two node pointers called ptrtonode are declared. The one inside the if statement points to an instance of a node object which is never deleted. The ptrtonode that does get deleted is null at the time of deletion. C++ guarantees that deleting a null pointer is harmless. Thus, a memory error detection tool may not flag this deletion as an error because it is a valid operation. A quick glance at the above function makes you think ptrtonode gets deleted, so there seems to be no problem. Whereas in actuality this might cause a serious memory leak.

Cause no. 4: Using delete on an Array

If we allocate an array with the array new operator (new[]) and then release the memory with the plain old delete operator it would result into a memory leak. For example:

char *ptr ;
ptr = new char[100];
delete ptr ; // NOT delete[] ptr ;

If you use the delete operator (and not the delete[] operator) to release an object that was allocated with new[], the resulting behavior is undefined. Even if you get away with this operation now it is likely to cause trouble later, or it may simply leak the memory.

Cause no. 5: Using careless Polymorphism

In C++ the destructors are not inherited. In C++, it is possible and in fact highly desirable to store the address of a derived-class object as a pointer to its base class. You get polymorphic behavior by referencing this derived class object through the base class pointer. The following program instantiates a derived-class object and stores the address as a pointer to its base class:

#include
class Base
{
public:
Base();
~Base();
};

class Derived : public Base
{
protected;
char *pBuf;
public:
Derived();
~Derived();
};

Base :: Base()
{
cout << "Base::Base()" << endl ;
}

Derived::Derived() : Base()
{
pBuf = new char [100];
cout << "Derived :: ~Derived()" << endl ;
}

Derived :: ~Derived()
{
delete[] pBuf ;
cout << "Derived :: ~Derived()" << endl ;
}

void main()
{
Base *pobject = new Derived() ;
delete pobject ;
}

On execution this program produces the following output:

Base::Base()
Derived::Derived()
Base::~Base()

Note that the destructor for the derived class did not get called. This would leak the buffer pBuf since it was allocated in the Derived constructor. To overcome this, simply declare the destructor of the Base class as a virtual function, as in:

class Base
{
public:
Base():
virtual ~Base();
} ;

The output of the program would now be:

Base::Base()
Derived::Derived()
Derived::~Derived()
Base::~Base()

Now the Derived class destructor is called, and Derived::pBuf is deleted.

No comments: