Constructors

In our first example in creating a class Timer, we used a member function called initialize( ). This was used to set the starting value of count to some legal value.

void initialize( )
{
cout<<"timer!";
count=0;
}

Basically, our idea was that whenever someone creates a Timer object the starting value of count shouldn’t have a garbage value. So we defined a member function which would assign values to our member data. Anyone who uses our class is expected to call the initialize( ) function first before using the Timer object. But there is a problem with this; when a user creates a Timer object he/she may forget to call the initialize function. This would create a lot of problems in the code later. Wouldn’t it be wonderful if we could initialize the member data of an object just like we initialize variables of fundamental data types?

Remember: Initialization and assignment are different. Initialization is done at the time of defining the variable. This is discussed at the end of this section.

Variables of fundamental data types can be initialized (at the time of declaration). For example:

    int variable = 5;

This line declares ‘variable’ to be an integer and assigns the value of 5 to ‘variable’.
Similarly, how about initializing an object? Constructors serve this purpose. When an instance of a class (or an object) is created, a special member function for that class called a ‘constructor’ is invoked. You can declare your own constructor but if you don’t then the compiler will provide a ‘default constructor’ for the class.

A constructor is a member function with same name as the class. It is usually used for the initialization of an object and it is executed at the time of creation of the object. Constructors cannot return any data.

Basically, objects are created to model real life entities and when an object is created, you will want some particular data (or properties) of the object to be initialized. The idea is to prevent an object from having an uncertain or undetermined state at the time of its creation. So, all initializations of member data are done using the constructor.

A constructor is just like a function except that you won’t have a return data type and also the name of the constructor should be the same as the name of the class.

General syntax for a constructor is:

constructor-name
{
//body of constructor
}

Another form that can be used is:

constructor-name ( ) : member-data (value)
{ }

Let's take a look at a simple program to illustrate the use of constructors.


class counter
{
private :
    int count;

public :
counter ( )        
// Constructor sets member data count to zero.
{
count=0;
}

//you can define more functions
};

int main ( )
{
counter c1;
return 0;
}

When the compiler reads the line

counter c1;

it goes to the public part of the class and sees the constructor used. Since

counter c1;

has no arguments the compiler makes use of the constructor without parameters. Hence the count of c1 is initialized to zero. This is what we wanted to do: Create an object and at the time of creation set the member data to a particular value. Instead of zero you can even give a value. For example:

counter ( )
{
count=7;
}

If you use the above constructor, then the value of the member data count is initialized to 7. The alternate form for this constructor is:

counter ( ) : count (7)
{ }

So, what’s the difference in the two forms? This form makes use of an initializer list (i.e. count(7) forms the initializer list over here) to initialize the values while the constructor body is empty. In the other method we initialize the members within the constructor body.

Remember: In the strictest sense, the first form:

counter ( )
{
count=7;
}

isn’t really an initialization (it is only an assignment). We’ll discuss this later.

Beware: When using an initialization list the order in which the members are initialized depends on the order in which you declare them within the class. The first member in the class will be the first member to be initialized. If your initializations depends on the sequence then be careful while using initializer lists.

In the case of assigning values within the constructor body, the order only depends on the order in which you write the statements within the body (and not on the declaration order).

This is an apt juncture at which we can discuss about initialization and assignment. Before going into classes, let’s consider our primitive (or fundamental) data types.

int amount;

    amount = 92;

In this case we have declared (and defined) a variable called ‘amount’. In the second statement, we assign a value of 92 to this variable. Now take a look at the following:

    int amount = 92;

In this case we have initialized the variable ‘amount’ with a value of 92. Initialization is done at the time of declaration.

You might recall that:

    int amount=92;

and

    int amount(92);

are equivalent.

Now let’s come back to our discussion of constructors.

counter ( )
{
count=7;
}

We are actually assigning a value to the member data count. This isn’t an initialization (the member ‘count’ was first defined and then later we are assigning a value of 7 to the member). But when you use an initializer list:

counter ( ) : count (7)
{ }

the member data ‘count’ is initialized with a value of 7. Generally, using an initializer list is more efficient than assignment but in the case of simple member data types this may not really matter. We’ll take a look at this topic again later.

If we do not provide a constructor, the compiler will provide its own default constructor (but this default constructor will not do anything useful - i.e. it will not initialize our member data). The compiler will not provide its default constructor if we provide our own constructor. A constructor with no parameters is also called a default constructor.

Constructors with Parameters (Parameterized Constructors):

Constructors are like functions and they can also take parameters just like other functions. An example is shown below:

class counter
{
private :
int count;

public :

counter (int x) : count (x)
{ }

// Some other functions in the class
};
int main ( )
{
int y;
counter c1(2);
cout<< "What value of count do you want initialise it to?";
cin>>y;
counter c2(y);

return 0;
}

The statement:

    counter c1(2);

means that the constructor with one parameter is invoked. The argument passed to the parameter is 2. This value is now assigned to the ‘count’ of object ‘c1’.

    count (x)

is equal to saying

    count = x.

Hence ‘count’ of object c1 is two. You could also write the constructor as:

counter (int x)
{
count=x;
}

Similarly,

    counter c2 (y);

means that ‘count’ of c2 will be initialized to the value of y (i.e. the value the user types in). You could also perform some validations within the constructor to ensure that the user doesn’t initialize the member data to an invalid value.

Remember: Constructors cannot return values.

Overloaded Constructors:

Constructors being similar to functions can also be overloaded. To overload a constructor the parameter type or the number of parameters in the constructors should be different. It is possible that in our program we might want to have one constructor with no arguments and one parameterized constructor to initialize the member data to some other values. This is illustrated below:

class rect
{
private:
int length, breadth;

public:
rect( )         //constructor with no parameter
{
length=breadth=0;
}

rect(int x, int y)         //constructor with parameters
{
length=x;
breadth=y;
}

};

int main( )
{
rect a;         //executes ‘no parameter constructor’
rect b(1,2);     //invokes the parameterized constructor
return 0;
}

The above program will create an object ‘a’ whose length and breadth will be initialized to 0. The object ‘b’ will have its length and breadth initialized to 1 and 2 respectively. Another way to write the above program would be as follows:

class rect
{
private:
    int length, breadth;

public:
    rect(int x=0, int y=0)    
    //if no argument is passed, x=0 and y=0.
    {
    length=x;
    breadth=y;
    }
};

int main( )
{
rect a;
rect b(1,2);
return 0;
}

The result will be the same as earlier. In this program we do not explicitly specify the default constructor. We make use of a single constructor to deal with both situations. If the program creates an object without any arguments then the constructor function will be executed with the default parameter values (specified to be 0 in this case). If an argument is passed to the constructor, then the parameter will be assigned the values of the argument.

Default Constructors and Arrays of Objects:

Just like creating an array of integer data type, we can also create an array of objects belonging to a particular class. When you create an array of objects the class should have a no argument constructor (in other words the class should have a default constructor).

Consider the following program:

# include <iostream.h>

class rect
{
private:
    int length, breadth;

public:
    rect(int x, int y)
    {
    length=x;
    breadth=y;
    }
};

int main( )
{
rect ob[3];
//ERROR
return 0;
}

In the above program we have a parameterized constructor but no default constructor. Thus the statement:

    rect ob[3];

will cause a compile time error. The reason why this does not work is because when you create an array of objects there is no way in which you can pass arguments to the parameterized constructor. The compiler will not know what to pass for each of the objects. Thus if you want to create an array of objects then the class should have a default constructor.

The program can be corrected as shown below:

class rect
{
private:
int length, breadth;
};

int main( )
{
rect ob[3];   
 //can be compiled without error
return 0;
}

In the above program there is a default constructor provided by the compiler itself. But usually it is better to provide our own default constructor (so that we can initialize the data) as below:

class rect
{
private:
int length, breadth;

public:
rect( )
            //default constructor
{
length=0;
breadth=0;
}

};

int main( )

{
rect ob[3];       
 //can be compiled
return 0;
}

Let’s recall the main points about default constructors:

If no constructor is provided, the compiler provides a default constructor (but this constructor does nothing).

If any constructor (even if only a parameterized constructor) is provided, the compiler will not provide a default constructor.

A constructor with no parameters is called a default constructor.

A constructor with default arguments is also a default constructor. For example: rect(int x=0, int y=0) //if no argument is passed, x=0 and y=0.

{
length=x;
breadth=y;
}

This is a default constructor.

Go back to the Contents Page

Copyright © 2005 Sethu Subramanian All rights reserved.