25C. Constructors


Defining constructors

Constructors are a feature 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;
    const char* name;

    Cell(int it, const 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. The expression's 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. Expression Cell(it, nm) has type 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 is clumsy. Just write
  Cell c(20, "a cell");



Issues with constructors

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 structure. For example, the constructor for Cell initializes both of the fields, item and name.

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.


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;
    const char* name;

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

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

    Cell()
    {
      item = 0;
      name = "";
    }
  };

Constructor requirement

If you define at least one constructor then each time you create a structured value of that type 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.

Watch out. In C++, statement

  Cell d();
does not create a Cell at all. It is interpreted as a prototype for a function called d.


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.)



Exercises

  1. Rewrite type Complex from the preceding page, providing 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

  6. A function is allowed to return a structure. Write a function that takes two complex numbers and returns their sum. Answer

  7. Redo the preceding exercise, but make your function return a pointer to a Complex structure. Answer