Tipos de constructores

El constructor es un método encargado de crear un objeto a partir de una clase, que por lo general, consiste en inicializar todos sus miembros. Los constructores tienen una serie de características interesantes que me gustaría resaltar:

  • Tienen el mismo nombre que la clase a la que pertenecen.
  • No tienen tipo de retorno, por lo que no retornan ningún valor.
  • No pueden ser llamados explícitamente por el programador.

Existen varios tipos de constructores:

  • Constructor por defecto.
  • Constructor con parámetros.
  • Constructor de copia.
  • Constructor de movimiento ( move constructor ).

Quiero dejar fuera de este post el move constructor porque merece un post aparte.

Como referencia vamos a utilizar una clase llamada Punto con dos miembros x e y.

class Punto {
    private:
        int x;
        int y;
};

Constructor por defecto

El constructor por defecto es un constructor sin parámetros.

Diferentes tipos de sintaxis:

Punto(); (1)
Punto()::Punto() { ... } (2)
Punto(int x = 0, int y = 0) { x(x), y(y) }; (3)
Punto() = delete; (4)
Punto() = default; (5)
Punto::Punto() = default; (6)
  1. Declaración de un constructor por defecto dentro de la definición de la clase.
  2. Definición del constructor fuera de la declaración de la clase. La clase debe contener un declaración. ( Lo que es lo mismo, el código va en el fichero .cc y la declaración en el .h)
  3. También es un constructor por defecto si no utilizamos los parámetros al crear el objeto.
  4. Borra el constructor por defecto. Si se necesita invocar un constructor por defecto falla la compilación.
  5. Define el constructor por defecto implícito incluso aunque hayan otros constructores presentes.
  6. Define el constructor por defecto fuera de la definición de la clase. El constructor se trata como definido por el usuario.

Cada vez que instanciamos un objeto sin parámetros se ejecuta el constructor por defecto.

Point p; //Se instancia en el stack un objeto del tipo Point.
Point *p = new Point; //Se declara en en la memoria dinámica un objeto del tipo Point

Constructor con parámetros

Como su nombre indica es un constructor que acepta diferentes parámetros que pueden ser sobrecargados.

En el caso de la clase Punto:

Punto(int x, int y); (1)
Punto(int x, int y) { x(x), y(y) }; (2)
Punto::Punto(int x, int y) { ... }; (3)
  1. Declaración de un constructor con parámetros.
  2. Declaración de un constructor con parámetros inicializado con una lista.
  3. Definición de un constructor fuera de la declaración de una clase. La clase debe contener una declaración.

Para que se ejecute el constructor con parámetros se instancia el objeto con parámetros.

Point p(0, 0); //Constructor con parámetros

Si tenemos varios constructores sobrecargados, se llamará el constructor correspondiente a los parámetros sobrecargados.

Constructor de copia

Este constructor se utiliza para crear un nuevo objeto a partir de otro objeto del mismo tipo. Es decir, inicializamos los miembros del nuevo objeto con los miembros del objeto que se pasa por parámetro.

Point(const Point &p); (1)
Point(const Point &p) = default; (2)
Point(const Point &p) = delete; (2)
  1. Declaración de un constructor de copia.
  2. Fuerza al compilador a generar un constructor de copia.
  3. Instruye al compilador a no generar un constructor de copia implícito.

Siguiendo con el ejemplo de la clase Point, el cuerpo del constructor de copia seria:

Point::Point(const Point &p) {
    this->x = p.x;
    this->y = p.y;
}

Las formas de utilizar un constructor de copia son:

Point p2(p1); (1)
Point p3 = p1; (2)
  1. Constructor de copia estándar.
  2. Inicialización directa. Se llama al constructor de copia.

Asignación de copia (Copy assignment)

La asignación de copia no es un constructor, es la sobrecarga del operador de igualdad. La asignación de copia consiste en copiar los miembros de un objeto en otro, con la diferencia de el objeto de destino ya está inicializado.

Point& operator = (const Point &p); (1)
Point& operator = (const Point &p) = default (2)
Point& operator = (const Point &p) = delete (3)
  1. Declaración de asignación de copia.
  2. Instruye al compilador a generar la asignación de copia por defecto.
  3. Borra la asignación de copia por defecto.

Veamos un ejemplo:

...
Point& operator = (const Point &p) {
    this->x = p.x;
    this->y = p.y;
}
...
Point A(0, 1);
Point B;

A = B; (1) //Aqui se llama la asignación de copia
Point C = B; (2) //Aquí se llama al constructor de copia

La diferencia entre ambas asignaciones es que (1) asigna (asignación de copia) un objeto en otro previamente inicializado mientras que en (2) el objeto no se ha creado, por lo tanto se llama al constructor de copia.

Enlaces

https://bytes.usc.edu/cs104/labs/lab3/

https://en.cppreference.com/w/cpp/language/default_constructor

https://www.fluentcpp.com/2019/04/19/compiler-generated-functions-rule-of-three-and-rule-of-five/