Chapter 12. Constructors and Destructors

Constructors and destructors are special forms of member functions.

Important

Constructor

A constructor is used to initialize an object

Destructor

A destructor is used to finalize an object ( = clean it up)

Constructors

When an object instance is created, all member variables have unitizialized values. To make proper use of an object these member variables should be given some values.

Since you do not want to set all attributes manually, we use "constructors".

Imagine the following class:

Figure 12.1. A Counter class
A Counter class

This class is very useful to count. It is (purposely) written so that the counter can not be set from the outside. Unfortunately C++ does not allow us to initialize variables with values. So how do we do it?

Answer: Constructors

Default Constructor

We define and implement a so called default constructor. A default constructor:

  • Is called by default when an object instance is created.

  • Needs no parameters

To define a default constructor we define a function with

  • the exact same name as the class

  • no parameters

  • no return type (not even void)

In most cases the default constructor will be public. However there are cases where you want it private (more about that later)

Usually consturctors are defined as the first methods. Usually default constructors are the first of the constructors

Example:

class Counter
{
private:
  int value;
public:
  Counter();
  // ...
};

Practice:

Define the following class, add a default constructor.

Figure 12.2. A daisy
A daisy

Implementing a default constructor is again the same as implementing that method that has the same name as your class.

What should you do in a constructor?

  • All member variables should be initialized

  • If a default value is given in the model, use that

  • If not, make up good starting values (0 for numbers, null for pointers).

  • If there is something else your object needs to do before it starts, this is the place to do it.

Example:

Counter::Counter()
{
  value = 0;
}

Practice:

Implement the default constructor for the Daisy class defined earlier.

Calling a default constructor:

A default construcotr is called everytime the object is initialized and we don't do anything special. Examples:

Counter *c;
c = new Counter;     // calls the constructor
Counter *c2 = new Counter();  // calls the constructor

Counter c3; // calls the constructor
Counter c4(); // calls the constructor

The parentensis () are optionional in this case. I prefer to use them to show visually that a constructor is called. You may do as you like.

Practice: Create a (dynamic reference to a) Daisy object and call its default constructor

Constructors with parameters

By this time at least one of you has heopfully already asked about it: What happens if we want to initialize variables, but we only know the values at construction? What if we need parameters to initialize the values to something that makes sense?

Example:

Figure 12.3. Location Class
Location Class

What should we use as starting values for x and y? 0 and 0 may be good values for a default constructor, but what we really want is a parameterized constructor where we can pass in the values.

Fortunately defining and implementing parameterized constructors is the same as default constructors, only that now we have parameters:

class Location
{
private:
  int x,y;
public:
  Location(int, int);
  // ...
};

...

Location::Location(int nx, int ny)
{
  x = nx;
  y = ny;
}

//alternate:
Location::Location(int x, int y)
{ 
  this->x = x;
  this->y = y;
}

Practice: Change "Daisy" to define an implement a constructor with one parameter (for number of leaves).

Just as with overloading (not overriding) we may have multiple constructors with different parameter lists. In most cases there will be a default constructor and one that takes parameters.

class Location
{
private:
  int x,y;
public:
  Location() { x = 0; y = 0; }
  Location(int nx, int ny) { x = nx; y = ny; }
  Location(int d) { x = d; y = d; }
  // ...
};

alternate, but not the SAME!!!!!!
  Location(int nx=0;int ny=0) { x = nx; y = ny; }

Calling a parameterized constructor:

We call parameterized constructors during object creation. Example:

Location *la = new Location(1,2);  // for dynamic objects
Location lb(4,5);                  // for static references

Practice: Create another new (dynamic) Daisy object and call its parameterized constructor.

Please note:

If we define no constructors, C++ will automatically provide an empty a default constructor.

If we define at least one constructor (default or parameterized) C++ will complain if we want to use the default constructor. This sometimes happen accidentaly.

Example:

Location l; // calls the default constructor
            // even though its not obvious

The copy constructor

This part is merely for completenes, but I will not ask you for that in any test. Assume Location object as given earlier, and all static object references.

Location la(1,2);
Location lb;
lb = la;

What happens here?

In the first line an object is created with the parameterized constructor.

In the secont line an object is created with the default constructor.

In the third line the object previousely referenced by lb is thrown away. A new object is created using the "copy constructor" to copy the variable values from la. This new object is then assigned to la.

C++ provides this copy constructor for us. If we want to override it we need to decare a constructor that takes one parameter whos type is a reference to the class type.

Example:

class Location
{
...
  Location(const Location &l); // const is optional but recommended
...
};