11.3.4. Alternativas a la construcción por copia

A estas alturas su cabeza debe estar echando humo, y se preguntará cómo es posible que pudiera escribir una clase que funcionase sin saber nada acerca del constructor de copia. No obstante, recuerde que el constructor de copia sólo es necesario cuando la clase se pasa por valor. Si esto no va a ocurrir, entonces no lo necesita.

Evitando el paso por valor

«Pero», puede decir, «si no defino el constructor de copia, el compilador lo creará por mí. ¿Cómo sé que un objeto nunca se pasará por valor?»

Existe una técnica simple para evitar el paso por valor: declare un constructor de copia private. Ni siquiera necesita definirlo (sólo declararlo), a no ser que un método o una función friend necesite realizar un paso por valor. Si el usuario intenta pasar o retornar el objeto por valor, el compilador se quejará con un error porque el constructor de copia es privado. El compilador ya no puede crear un constructor de copia por defecto porque explícitamente ya hay uno creado.

He aquí un ejemplo:

//: C11:NoCopyConstruction.cpp
// Preventing copy-construction

class NoCC {
  int i;
  NoCC(const NoCC&); // No definition
public:
  NoCC(int ii = 0) : i(ii) {}
};

void f(NoCC);

int main() {
  NoCC n;
//! f(n); // Error: copy-constructor called
//! NoCC n2 = n; // Error: c-c called
//! NoCC n3(n); // Error: c-c called
} ///:~

Listado 11.10. C11/NoCopyConstruction.cpp


Note la utilización de la forma más general

NoCC(const NoCC&);

utilizando const

Funciones que modifican objetos externos

La sintaxis de referencias es más agradable que la de punteros, aunque oculte significado al que lea el código fuente. Por ejemplo, en la librería iostreams existe una versión sobrecargada de la función get() que tiene como argumento un char &, y su cometido es modificar ese argumento y utilizarlo como el valor que retorna get(). No obstante, si lee el código fuente de esta función, no es inmediatamente obvio que la variable que se pasa como argumento vaya a ser modificada:

char c;
cin.get(c);

Parece que a la función se le pasa por valor, lo que sugiere que el argumento que se pasa no se modifica.

A causa de esto, es probablemente más seguro, desde el punto de vista de mantenimiento del código fuente, utilizar punteros que pasen la dirección del argumento que se desee modificar. Si siempre pasa direcciones como referencias constantes excepto cuando intenta modificar el argumento que se pasa a través de la dirección, donde pasaría un puntero no constante, entonces es más fácil para el lector seguir el código fuente.