Wednesday, 27 January 2010

Memory Allocation example for Double Pointer of a class

The following program should be self explanatory:



//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
//Program to demonstrate memory allocation to double pointers
#include<iostream>

using namespace
std;

//Class named NewClass
class NewClass
{

public
:
int
a;
int
* b;
void
* c;
};


//List Structure containing multiple NewClass objects
struct NewClassList
{

int
numOfObjects;
NewClass** Objects;
};


int
main()
{

//Create NewClassList with 4 objects with following values:
//1, 10, 100; 2, 20; 200; 3, 30, 300; 4, 40, 400;
NewClassList someList;
someList.numOfObjects = 4;
//someList.Objects = new NewClass*; //1. Common mistake in allocating memory
someList.Objects = new NewClass*[someList.numOfObjects];

someList.Objects[0] = new NewClass;
someList.Objects[0]->a = 1;
someList.Objects[0]->b = new int(10);
someList.Objects[0]->c = (void*) new int(100);

someList.Objects[1] = new NewClass;
someList.Objects[1]->a = 2;
someList.Objects[1]->b = new int(20);
someList.Objects[1]->c = (void*) new int(200);

someList.Objects[2] = new NewClass;
someList.Objects[2]->a = 3;
someList.Objects[2]->b = new int(30);
someList.Objects[2]->c = (void*) new int(300);

someList.Objects[3] = new NewClass;
someList.Objects[3]->a = 4;
someList.Objects[3]->b = new int(40);
someList.Objects[3]->c = (void*) new int(400);

//Printing the outputs
for(int i = 0; i < someList.numOfObjects; i++)
{

cout<<"Iteration = "<<i;
cout<<": a = "<<someList.Objects[i]->a;
cout<<": b = "<<*(someList.Objects[i]->b);
cout<<": c = "<<*((int*)someList.Objects[i]->c)<<endl;
}


//Clear all the memory allocation
for(int i = 0; i < someList.numOfObjects; i++)
{

delete
someList.Objects[i]->b;
delete
someList.Objects[i]->c;
delete
someList.Objects[i];
}

delete
[] someList.Objects;
return
0;
}





The output is as follows:

Tuesday, 19 January 2010

The Unmanaged Pointer problem

This is an interesting problem from More Exceptional C++: 40 New Engineering Puzzles, Programming Problems, and Solutions by Herb Sutter. See Amazon link at the bottom of the post.

In your travels through the dusty corners of your company's code archives, you find the following code fragment:

// Example 20-2
//

// In some header file:
void f( T1*, T2* );

// In some implementation file:
f( new T1, new T2 );

Does this code have any potential exception safety problems?

On a personal note, I have seen people trying to do as much as possible on a single line thinking that they are writing optimised code. This may be true in some very rare instances but in most of the cases this is not true. Performing multiple operations in a single statement can cause side effects like memory leaks or incorrect operation, it can also make the code less readable and can cause difficulty in debugging.

Going back to the problem at hand, according to Herb Sutter there are several potential exception safety problems with the above code. An expression such as new T1 is called, simply enough, a new-expression. Recall what a new-expression really does:

* It allocates memory;
* It constructs a new object in that memory; and
* If the construction fails because of an exception the allocated memory is freed.

So each new-expression is essentially a series of two function calls: one call to operator new() (either the global one, or one provided by the type of the object being created), and then a call to the constructor.

So in case of a function defined as f( expr1, expr2 );

consider what happens if the compiler decides to generate code as follows:

1. allocate memory for the T1
2. construct the T1
3. allocate memory for the T2
4. construct the T2
5. call f()

The problem is this: If either step 3 or step 4 fails because of an exception, the C++ standard does not require that the T1 object be destroyed and its memory deallocated. This is a classic memory leak, and clearly Not a Good Thing.

Another possible sequence of events is the following:

1. allocate memory for the T1
2. allocate memory for the T2
3. construct the T1
4. construct the T2
5. call f()

This sequence has not one, but two exception safety problems with different effects:

If step 3 fails because of an exception, then the memory allocated for the T1 object is automatically deallocated (step 1 is undone), but the standard does not require that the memory allocated for the T2 object be deallocated. The memory is leaked.

If step 4 fails because of an exception, then the T1 object has been allocated and fully constructed, but the standard does not require that it be destroyed and its memory deallocated. The T1 object is leaked.

As I have mentioned that performing multiple operations in a single statement is potentially harmful. If you have no option and want to go the above way then the above mentioned problems can be avoided by the use of auto_ptr. See here.

Check out the book on Amazon:



Tuesday, 12 January 2010

Order of Evaluation of functions

Consider the following code:





//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
#include<iostream>

using namespace
std;

class
Base{
public
:
Base():x(0){};
int
incrAndGetX(void) {return ++x;};
int
decrAndGetX(void) {return --x;};
int
x;
};


int
Add(int a, int b)
{

return
(a+b);
}


int
Sub(int a, int b)
{

return
(a-b);
}


int
main()
{

Base b;
int
val1 = Add(b.incrAndGetX(), b.decrAndGetX());
int
val2 = Sub(b.incrAndGetX(), b.decrAndGetX());

cout<<"Val 1 = "<<val1<<endl;
cout<<"Val 2 = "<<val2<<endl;

return
0;
}







What is the output of the following program?

Depending on the compiler the output can be different. I tried it using the g++ and MSVC8 compilers and got the same result but the result cannot be guaranteed. I got the following output:

Val 1 = -1
Val 2 = 1

As you will notice that the order of evaluation is from right to left. Depending on the compiler and the platform the order of evaluation could be left to right, right to left or interleaved. The kind of problem shown in the program above can be dangerous as your code will work on 99% of the PCs and will fail on 1% and you will invest lots of time and resources to find and fix the problem.

There is an interesting discussion here on the same topic if you want to explore this topic further.