Showing posts with label Constructor. Show all posts
Showing posts with label Constructor. Show all posts

Wednesday, 2 June 2010

Class initialisation and constructors

There is often lots of confusion with regards to class initialisation. People may forget to take care while using default constructors and copy constructors. Here is a simple example that tries to explain lots of concepts.





//Program tested on Microsoft Visual Studio 2008 - Zahid Ghadialy
//This program gives an example of constructors and initialisations

#include<iostream>

using namespace
std;

//Example of a class
class someClass1
{

public
:
//implicit default constructor
int x;
int
*y;
};


//Example of a class with constructor
class someClass2
{

public
:
someClass2(const someClass2& xyz)
{

cout<<"** copy constructor called"<<endl;
x = xyz.x;
y = new int(*xyz.y);
}

//default constructor will have to be explicitly defined
someClass2() {};
//overloading operator '='
const someClass2& operator = (const someClass2& xyz)
{

cout<<"** operator '=' called"<<endl;
x = xyz.x;
y = new int(*xyz.y);
return
*this;
}

int
x;
int
*y;
};


int
main()
{

someClass1 a; //Default initialisation
a.x = 1234;
a.y = new int(6789);

cout<<"someClass1: a.x = "<<a.x<<" a.y( " <<a.y<< " ) = "<<*(a.y)<<endl;

someClass1 b = a; //Copy Initialisation

cout<<"someClass1: b.x = "<<b.x<<" b.y( " <<b.y<< " ) = "<<*(b.y)<<endl;

someClass1 c(a); //Direct Initialisation

cout<<"someClass1: c.x = "<<c.x<<" c.y( " <<c.y<< " ) = "<<*(c.y)<<endl;

//Calling default constructor
someClass2 aa;
aa.x = 2468;
aa.y = new int(3579);

cout<<"someClass2: aa.x = "<<aa.x<<" aa.y( " <<aa.y<< " ) = "<<*(aa.y)<<endl;

//calling copy constructor
someClass2 bb = aa; //Copy Initialisation - note copy constructor will be called

cout<<"someClass2: bb.x = "<<bb.x<<" bb.y( " <<bb.y<< " ) = "<<*(bb.y)<<endl;

//calling copy constructor
someClass2 cc(aa); //Direct Initialisation - note copy constructor called in this case as well

cout<<"someClass2: cc.x = "<<cc.x<<" cc.y( " <<cc.y<< " ) = "<<*(cc.y)<<endl;

someClass2 dd;
//calling operator =
dd = aa;

cout<<"someClass2: dd.x = "<<dd.x<<" dd.y( " <<dd.y<< " ) = "<<*(dd.y)<<endl;

return
0;
}







The output is as follows:

There are certain things worth noting in the above example:
  • In case of someClass1, since only the default constructor is used, the same pointer is used for y in all the cases. This can cause serious problems in the code if one of the classes delete the memory pointed by y for class someClass1. This problem is not present in someClass2
  • If any constructor is defined, it becomes necessary to define the default constructor. You can make sure that nobody uses default constructor in case of someClass2 by making it private (I havent done that because I wanted to show operator =)
  • As you can see in case of variable cc, copy constructor would always be called instead of operator =.
  • operator = would only be called in case of assignment. This is a common mistake.
  • In the code above, cc(aa) is better than using bb = aa even though the results are the same. The main advantage being that it will avoid confusion with operator '=' if its overloaded for a novice and the constructor can be overloaded to take more than one input in future.

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:



Thursday, 18 June 2009

Copy Constructor and Assignment Operator

This example shows the Copy Constructor and the Assignment Operator. In the example, I have just relied on the Default Copy Constructor and the Default Assignment Operator. For a complex class, the default may not be a good option and they will have to be explicitly written.


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

using namespace
std;

class
A {
public
:
int
x,y;
};


int
main()
{

A a;
a.x = 11;
a.y = 2222;

A a2(a); //Default Copy constructor called
//Copies a paramaters to a2
cout<<"a2 variables are: a2.x = "<<a2.x<<" and a2.y = "<<a2.y<<endl;

A a3;
a3 = a; //Default Assignment Operator called
//Copies a paramaters to a3
cout<<"a3 variables are: a3.x = "<<a3.x<<" and a3.y = "<<a3.y<<endl;

return
0;
}



The output is as follows: