¿De qué sirve el constructor de copia privada en c ++?

¿Por qué las personas definen un constructor de copia privada?

¿Cuándo se hace un buen diseño al constructor de copias y al operador de asignación privado?

Si no hay miembros en la clase que sean punteros o manejadores de un objeto único (como el nombre del archivo), entonces ¿hay otros casos en que el constructor de copia privada sea una buena idea?

La misma pregunta se aplica al operador de asignación. Dado que la mayoría de C ++ gira en torno a la copia de objetos y el paso por referencia, ¿hay algún buen diseño que involucre un constructor de copia privada?

Algunos objetos representan entidades particulares que no pueden o no deben copiarse. Por ejemplo, puede evitar la copia de un objeto que representa el archivo de registro utilizado por una aplicación, lo que corresponde a la expectativa de que todas las partes del código utilizarán un único archivo de registro. El uso de un objeto copiado accidentalmente o incorrectamente podría provocar que el contenido fuera de servicio aparezca en el registro, registros inexactos del tamaño actual del registro, bashs múltiples (algunos errores) de “rodar” a un nuevo nombre de archivo o renombrar el existente.

Otro uso es hacer cumplir la copia a través de una función virtual. Como los constructores no pueden ser virtual , una práctica común es evitar el acceso directo al constructor de copias y proporcionar un método virtual Base* clone() que devuelve una copia del tipo de tiempo de ejecución real al que señala un puntero. Esto evita el corte accidental que exhibiría la Base b(derived) .

Otro ejemplo: un objeto de puntero inteligente muerto simple que simplemente elimina el puntero que se le da en el constructor: si no admite el recuento de referencias u otra forma de manejar varios propietarios, y no desea tener un riesgo incómodo std::auto_ptr transferencia de propiedad de estilo std::auto_ptr , luego simplemente ocultando el constructor de copia se obtiene un gran pequeño puntero inteligente que es rápido y eficiente para los casos limitados en los que es utilizable. Un error de tiempo de comstackción sobre el bash de copiarlo efectivamente le preguntaría al progtwigdor “oye – si realmente quieres hacer eso, cámbiame a un puntero compartido, de lo contrario retrocede”.

Un caso de uso es el patrón singleton donde solo puede haber exactamente una instancia de una clase . En este caso, debe hacer que sus constructores y el operador de asignación = privado de modo que no haya forma de crear más de un objeto. La única forma de crear un objeto es a través de su función GetInstance () como se muestra a continuación.

 // An example of singleton pattern class CMySingleton { public: static CMySingleton& GetInstance() { static CMySingleton singleton; return singleton; } // Other non-static member functions private: CMySingleton() {} // Private constructor ~CMySingleton() {} CMySingleton(const CMySingleton&); // Prevent copy-construction CMySingleton& operator=(const CMySingleton&); // Prevent assignment }; int main(int argc, char* argv[]) { // create a single instance of the class CMySingleton &object = CMySingleton::GetInstance(); // compile fail due to private constructor CMySingleton object1; // compile fail due to private copy constructor CMySingleton object2(object); // compile fail due to private assignment operator object1 = object; // .. return 0; } 

Un muy mal ejemplo:

 class Vehicle : { int wheels; Vehicle(int w) : wheels(w) {} } class Car : public Vehicle { Engine * engine; public Car(Engine * e) : Vehicle(4), engine(e) } ... Car c(new Engine()); Car c2(c); // Now both cars share the same engine! Vehicle v; v = c; // This doesn't even make any sense; all you have is a Vehicle with 4 wheels but no engine. 

¿Qué significa “copiar” un automóvil? (¿Es un automóvil un modelo de automóvil o una instancia de automóvil? ¿Copiarlo conserva el registro del vehículo?)

¿Qué significa asignar un vehículo a otro?

Si las operaciones no tienen sentido (o simplemente no se implementan), lo estándar para hacer es hacer que el constructor de copia y el operador de asignación sean privados, causando un error de comstackción si se usan en lugar de un comportamiento extraño.

Una razón común para hacer que el constructor de copia y la asignación de copia sean privados es deshabilitar la implementación predeterminada de estas operaciones. Sin embargo, en C ++ 0x hay una syntax especial = eliminar para tal fin. Entonces, en C ++ 0x, hacer copy ctor private parece resrtarse a casos muy exóticos.

Copiar ctors y asignaciones son azúcar sintáctico; así que tal “azúcar privado” parece ser un síntoma de avaricia 🙂

Incluso si los contenidos del objeto no son punteros ni otras referencias, puede ser útil evitar que las personas copien el objeto. Tal vez la clase contenga una gran cantidad de datos, y la copia es una operación demasiado pesada.

Es posible que desee implementar algunos de los métodos de la clase utilizando un constructor de copia, pero no para exponerlo fuera de la clase. Entonces lo haces privado. Como cualquier otro método.

El ” idioma de constructor virtual ” es un caso importante en el que se necesita un constructor de copia privada o protegida. Surge un problema en C ++ donde se le da el puntero a una clase base, de un objeto que realmente es heredado de esta clase base, y desea hacer una copia de él. Llamar al constructor de copia no llamaría al constructor de copia de la clase heredada, sino que llamaría al constructor de copia de la clase base.

Observar:

 class Base { public: Base( const Base & ref ){ std::cout << "Base copy constructor" ; } }; class Derived : public Base { public: Derived( const Derived & ref ) : Base(ref) { std::cout << "Derived copy constructor"; } } Base * obj = new Derived; Base * obj2 = new Derived(*obj); 

El código anterior produciría el resultado:

 "Base copy constructor" 

¡Claramente este no es el comportamiento que el progtwigdor quería! El progtwigdor estaba intentando copiar un objeto de tipo "Derivado" pero en su lugar obtuvo un objeto de tipo "Base".

El problema se soluciona mediante el uso de la expresión idiomática antes mencionada. Observe el ejemplo escrito arriba, reescrito para usar este modismo:

 class Base { public: virtual Base * clone () const = 0; //this will need to be implemented by derived class protected: Base( const Base & ref ){ std::cout << "Base copy constructor" ; } }; class Derived : public Base { public: virtual Base * clone () const { //call private copy constructor of class "Derived" return static_cast( new Derived(*this) ); } //private copy constructor: private: Derived( const Derived & ref ) : Base(ref) { std::cout << "Derived copy constructor"; } } Base * obj = new Derived; Base * obj2 = obj->clone(); 

El código anterior produciría el resultado:

 "Base copy constructor" "Derived copy constructor" 

En otras palabras, el objeto que se construyó en el tipo deseado "Derivado", y no del tipo "Base"!

Como puede ver, en el tipo Derivado, el constructor de copia se hizo intencionalmente privado, porque sería un diseño de API incorrecto darles a los progtwigdores la capacidad de intentar accidentalmente llamar al constructor de copia manualmente, en lugar de usar la interfaz inteligente proporcionada por clone ( ) Dicho de otra forma, un constructor de copia pública directamente disponible podría hacer que los progtwigdores cometieran el error mencionado en la parte 1. En este caso, la mejor práctica tendría el constructor de copia oculto a la vista, y solo indirectamente accesible mediante el método "clone ( ) ".