14.10.2. FIXME Upcasting y el constructor de copia

Si permite que el compilador cree un constructor copia de una clase derivada, éste llamara automáticamente al constructor copia de la clase base, y entones ,a los constructores copia para todos los miembros objeto (o realizará una copia de bits en los tipos predefinidos) entonces conseguirá la conducta correcta:

//: C14:CopyConstructor.cpp
// Correctly creating the copy-constructor
#include <iostream>
using namespace std;

class Parent {
  int i;
public:
  Parent(int ii) : i(ii) {
    cout << "Parent(int ii)\n";
  }
  Parent(const Parent& b) : i(b.i) {
    cout << "Parent(const Parent&)\n";
  }
  Parent() : i(0) { cout << "Parent()\n"; }
  friend ostream&
    operator<<(ostream& os, const Parent& b) {
    return os << "Parent: " << b.i << endl;
  }
};

class Member {
  int i;
public:
  Member(int ii) : i(ii) {
    cout << "Member(int ii)\n";
  }
  Member(const Member& m) : i(m.i) {
    cout << "Member(const Member&)\n";
  }
  friend ostream&
    operator<<(ostream& os, const Member& m) {
    return os << "Member: " << m.i << endl;
  }
};

class Child : public Parent {
  int i;
  Member m;
public:
  Child(int ii) : Parent(ii), i(ii), m(ii) {
    cout << "Child(int ii)\n";
  }
  friend ostream&
    operator<<(ostream& os, const Child& c){
    return os << (Parent&)c << c.m
              << "Child: " << c.i << endl;
  }
};

int main() {
  Child c(2);
  cout << "calling copy-constructor: " << endl;
  Child c2 = c; // Calls copy-constructor
  cout << "values in c2:\n" << c2;
} ///:~

Listado 14.18. C14/CopyConstructor.cpp


El operador<< de Child es interesante por la forma en que llama al operador<< del padre dentro de éste: convirtiendo el objeto Child a Parent& (si lo convierte a un objeto de la clase base en vez de una referencia, probablemente obtendrá resultados no deseados)

return os << (Parent&)c << c.m

Dado que el compilador lo ve como Parent, éste llama al operador<< Parent.

Puede observar que Child no tiene explícitamente definido un constructor copia. El compilador crea el constructor copia (es una de las cuatro funciones que sintetiza, junto con el constructor del defecto - si no creas a ninguna constructores - el operator= y el destructor) llamando el constructor copia de Parent y el constructor copia de Member. Esto muestra la siguiente salida

Parent(int ii)
Member(int ii)
Child(int ii)
calling copy-constructor:
Parent(const Parent&)
Member(const Member&)
values in c2:
Parent: 2
Member: 2
Child: 2

Sin embargo, si escribe su propio constructor copia para Child puede tener un error inocente y funcionar incorrectamente:

Child(const Child& c) : i(c.i), m(c.m) {}

entonces el constructor por defecto será llamado automáticamente por la clase base por parte de Child, aquí es dónde el compilador muestra un error cuando no tienen otra (recuerde que siempre algun constructor se ejecuta para cada objeto, sin importar si es un subobjeto de otra clase). La salida será entonces:

Parent(int ii)
Member(int ii)
Child(int ii)
calling copy-constructor:
Parent()
Member(const Member&)
values in c2:
Parent: 0
Member: 2
Child: 2

Esto probablemente no es lo que espera, generalmente deseará que la parte de la clase base sea copiada del objeto existente al nuevo objeto como parte del constructor copia.

Para arreglar el problema debe recordar como funciona la llamada al constructor copia de la clase base (como el compilador lo hace) para que escriba su propio constructor copia. Este puede parecer un poco extraño a primera vista pero es otro ejemplo de upcasting.

Child(const Child& c)
   : Parent(c), i(c.i), m(c.m) {
   cout << "Child(Child&)\n";
}

La parte extraña es cómo el constructor copia es ejecutado: Parent(c). ¿Qué significa pasar un objeto Child al constructor padre? Child hereda de Parent, entonces una referencia de Child es una referencia Parent. El constructor copia de la clase base convierte a una referencia de Child a una referencia de Parent y la utiliza en el construcción de la copia. Cuando escribe su propio constructor copia la mayoría de ocasiones deseará lo mismo.