Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


Copiar objetos: ejemplo


El presente ejemplo es continuación de otro anterior, correspondiente a constructor de oficio. En el que presentamos ahora, se muestra la influencia y alcance de la declaración de un constructor-copia explícito, aunque haremos mención de los resultados obtenidos en el original, dado que en ocasiones el compilador incluye por su cuenta invocaciones a los constructores de oficio de algunos miembros.

El primer caso muestra como el constructor-copia oficial garantiza una copia física (todos los miembros a todos los niveles) del objeto original.

#include <iostream>
using namespace std;

struct Nombre {
  char* nomb;
  Nombre() { nomb = "Anonimo"; }  // constructor explícito
};

struct Equipo {
  Nombre nm;
  size_t sz;
};

struct Liga {
  int year;
  char categoria;
  Nombre nLiga;
  Equipo equipos[10];
  Liga() {         // constructor explícito
    nLiga.nomb = "Primera division";
    year = 2003;
    categoria = 'A';
  }
};

int main() {       // ===========
  Liga primDiv;    // M1:
  primDiv.equipos[0].sz = 33;
  cout << "S1: " << primDiv.year << endl;
  cout << "S2: " << primDiv.categoria << endl;
  cout << "S3: " << primDiv.nLiga.nomb << endl;
  cout << "S4: " << primDiv.equipos[0].nm.nomb << endl;
  cout << "S5: " << primDiv.equipos[0].sz << endl;
  cout << "S6: " << primDiv.equipos[9].nm.nomb << endl;
  cout << "S7: " << primDiv.equipos[9].sz << endl;

  Liga segDiv = primDiv;  // M11:
  cout << "S1a: " << segDiv.year << endl;
  cout << "S2a: " << segDiv.categoria << endl;
  cout << "S3a: " << segDiv.nLiga.nomb << endl;
  cout << "S4a: " << segDiv.equipos[0].nm.nomb << endl;
  cout << "S5a: " << segDiv.equipos[0].sz << endl;
  cout << "S6a: " << segDiv.equipos[9].nm.nomb << endl;
  cout << "S7a: " << segDiv.equipos[9].sz << endl;
  return 0;
}

Salida:

S1: 2003
S2: A
S3: Primera division
S4: Anonimo
S5: 33
S6: Anonimo
S7: 4205790

S1a: 2003
S2a: A
S3a: Primera division
S4a: Anonimo
S5a: 33
S6a: Anonimo
S7a: 4205790

Comentario

La sentencia M1 instancia un objeto utilizando el constructor por defecto (explícito). A continuación se muestran sus propiedades, incluyendo algunos miembros de las que tienen una estructura compleja por ser a su vez tipos abstractos. En M11 se crea otro objeto, un clon del anterior, que es realizado por el constructor-copia proporcionado por el compilador. Las sentencias que siguen muestran claramente como este constructor-copia de oficio realiza una copia profunda (miembro-a-miembro) del objeto. Al comentar el ejemplo original ( Ejemplo) señalamos que las salidas S7 y S7a contienen basura por falta de un iniciador adecuado.

Para evaluar la influencia de un constructor-copia, modificamos el diseño de la clase Liga.  El resto del diseño permanece exactamente igual:

struct Liga {
  int year;
  char categoria;
  Nombre nLiga;
  Equipo equipos[10];
  Liga() {             // constructor por defecto
    nLiga.nomb = "Primera division";
    year = 2003;
    categoria = 'A';
  }
  Liga(Liga& lig) {    // constructor-copia
    categoria = lig.categoria;
  }
};

Nueva salida:

S1: 2003
S2: A
S3: Primera division
S4: Anonimo
S5: 33
S6: Anonimo
S7: 4205790

S1a: 4199740
S2a: A
S3a: Anonimo
S4a: Anonimo
S5a: 2
S6a: Anonimo
S7a: 4205790

Comentario

La definición proporcionada para el constructor-copia solo incluye una asignación explícita para el miembro Liga::categoria. Todos los demás quedan indefinidos. Este miembro queda reproducido correctamente en el nuevo objeto (salida S2a). Para el resto de miembros no especificados, el compilador asume que deseamos el valor correspondiente al constructor por defecto. En consecuencia, podemos suponer que los miembros abstractos no especificados del nuevo objeto responden a las asignaciones [1]:

Liga::year = int();
Liga::nLiga = Nombre();
Liga::equipos[0] = Equipo();
Liga::equipos[1] = Equipo();
...
Liga::equipos[9] = Equipo();

El miembro Liga::year es un tipo simple al que su constructor por defecto no aplica ninguna inicialización específica. En consecuencia el miembro segDiv.year contiene basura (salida S5a).

El constructor por defecto de la clase Nombre si está definido, e inicia a todos los miembros Nombre::nomb con la misma cadena.

El constructor por defecto Equipo::Equipo() invoca a su vez al constructor por defecto de la clase Nombre para iniciar el miembro Equipo::nm.

El miembro Equipo::sz es un tipo simple, cuyo constructor por defecto no realiza ninguna inicialización específica, y en consecuencia contiene basura (salidas S5a y S7a).

  Inicio.


[1]  Se trata de expresiones méramente didácticas que intentan expresar una idea, aunque son incorrectas desde el punto de vista de su sintaxis.