5.13.3. Constructors and Destructors


Defining constructors

Constructors are a feature of C++ (but not C) that make initialization of structures convenient. Within a structure type definition, define a constructor in a way that is similar to a function definition, with the following differences.

  1. The name of the constructor must be the same as the name of the structure type.

  2. Do not write a return type. The definition starts with the name of the constructor.

  3. In the body of the constructor, refer to the fields by their names. Do not use a dot. The fields are implicitly those of the structure that is being initialized.

For example,

  struct Cell
  {
    int   item;
    char* name;

    Cell(int it, char* nm)
    {
      item = it;
      name = nm;
    }
  };
defines type Cell with two fields and one constructor that takes two parameters.


Using constructors

You can use a constructor in three ways, which we illustrate for type Cell.

Cell c(20, "a cell");

This is equivalent to
  Cell c;
  c.item = 20;
  c.name = "a cell";
since the constructor definition's body says to do
  item = it;
  name = nm;
where it and nm are the parameters. In general, writing parameters after a variable name in a statement that creates the variable runs the constructor with the given parameters after creating the variable.

Cell* p = new Cell(20, "a cell");

Expression new Cell(it,nm) creates a new cell in the heap and runs the constructor on that new cell, with the given parameters. Its value is a pointer to the new cell. Notice that, in this case, the parameters are written after new Cell, not after the name of the pointer variable p.

Expression new Cell(it, nm) has type Cell*.


Cell(20, "a cell");

Expression Cell(it, nm) creates a cell in the frame of the current function and initializes that cell. The type of expression Cell(it, nm) is Cell.

You will not find this form very useful. Be careful not to confuse it with new Cell(it, nm), which yields a pointer (type Cell*) to a new cell that is in the heap.

You might be tempted to write

  Cell c = Cell(20, "a cell");
That works, but it asks to create a cell, initialize that cell, and then to copy that cell into c. The preferred way to do that is to initialize c directly:
  Cell c(20, "a cell");



Field names

The presence of a constructor does not alter the names of the fields. Type Cell above has two fields, called item and name. It does not make sense to write

  Cell c;
  c.it = 34;
since a Cell does not have a field called it. The constructor happens to have a parameter called it.


What to initialize in a constructor

It is good practice to ensure that a constructor initializes all of the fields of a structured value. For example, the constructor for Cell initializes both of the fields, item and name, of a Cell.

It is not necessary for a constructor to have as many parameters as there are fields, but it often does. If you have fewer parameters than fields, you typically initialize some of the fields to default values.


Constructor requirement

If you define a constructor in the type definition, then you are required to use a constructor when you create a value of that type. For example, the definition of type Cell above has a constructor with two parameters. Statement

  Cell c(20, "a cell");
is allowed since it uses the constructor, but statement
  Cell c;
is not allowed because it does not use a constructor.


Multiple constructors

A structure type definition can include more than one constructor, as long as no two constructors have the same number and types of parameters. For example, an alternative definition of type Cell with three constructors is as follows.

  struct Cell
  {
    int   item;
    char* name;

    Cell(int it, char* nm)
    {
      item = it;
      name = nm;
    }

    Cell(int it)
    {
      item = it;
      name = "";
    }

    Cell()
    {
      item = 0;
      name = "";
    }
  };
If there are multiple constructors then each time you create a structured value you must use one of the constructors. For example,
  Cell a(16, "kangaroo");
  Cell b(3);
uses the constructor with two parameters to create a and the constructor with one parameter to create b.


Parameterless constructors

To use a parameterless constructor, do not write ( ). For example,

  Cell c;
creates a Cell c and initializes it using the constructor with no parameters. Similarly,
  Cell* p = new Cell;
creates a new Cell in the heap and initializes that cell using the parameterless constructor. In C++, statement
  Cell d();
does not create a Cell at all. It is interpreted as a prototype for a function called d that takes no parameters and yields a result of type Cell.


Constructors and arrays

If you create an array of a structure type, then each thing in the array is automatically initialized using the parameterless constructor. For example,

  Cell A[10];
creates an array of Cell values and initializes each of them using the parameterless constructor.

Note. If there is at least one constructor and no parameterless constructor then you are not allowed to create an array of structures of that type.

Note. An array of pointers is not automatically initialized. So

  Cell* P[10];
creates an array of uninitialized pointer variables. (The pointers are dangling until you store something into them.)


Destructors

You are allowed to include something called a destructor, or also a deconstructor, in a structure type definition. Any time a structured value of that type is destroyed, either automatically or explicitly, the destructor is run on the structured value first. Typically, the destructor deallocates memory that is pointed to by pointers within the structured value. The destructor for type T is called ~T and always has no parameters. For example,

  struct Vect
  {
    int  size;
    int* arr;

    Vect(int n)
    {
      size = n;
      arr  = new int[n];
    }

    ~Vect()
    {
      delete [] arr;
    }
  };
allocates an array when a Vect value is created and deallocates that array when the Vect value is destroyed (for any reason).


Exercises

  1. A complex number is represented by a pair of real numbers called its real part and its imaginary part. Define a structure type called Complex with two fields called impart and repart. Provide two constructors: one takes two parameters of type double and installs them into the two fields; the other takes no parameters and sets both fields to 0.0. Answer

  2. Using type Complex from the preceding exercise, write a statement that creates a new complex number whose real part is 1.0 and whose imaginary part is 2.5. Call the new complex number z, and put it in the frame for the current function. Answer

  3. Repeat the previous exercise, but this time allocate the complex number in the heap, and make z a pointer to it. Answer

  4. If you create an array of Complex values, how are the values in the array initialized? Answer

  5. Write a statement that creates variable zero of type Complex. Initialize it using the parameterless constructor. Answer