2012-01-29 8 views
11

Por lo que entiendo que soy capaz de "desactivar" copiar y asignar a mis objetos mediante la definición de constructor de copia privada y operador de asignación:No permitir misiones y paso por valor

class MyClass 
{ 
private: 
    MyClass(const MyClass& srcMyClass); 
    MyClass& operator=(const MyClass& srcMyClass); 
} 

Pero ¿cuál es el uso de esta?
¿Se considera una mala práctica?

Le agradecería si pudiera describir la situación, en la cual sería razonable/útil "deshabilitar" la tarea y copiar el constructor de esta manera.

+1

A singleton sería un ejemplo. –

Respuesta

11

Es útil cuando no tiene sentido copiar su objeto. Definitivamente no se considera una mala práctica.

Por ejemplo, si tiene una clase que representa una conexión de red, no tiene sentido copiar ese objeto. En otra ocasión, es posible que desee que una clase no se pueda copiar si tiene una clase que representa a un jugador en un juego de varios jugadores. Ambas clases representan cosas que no se pueden copiar en el mundo real, o que no tiene sentido copiar (una persona, una conexión).

Además, si está tratando de implementar un Singleton, es un procedimiento estándar para hacer que los objetos no se puedan copiar.

+0

Gracias, tiene sentido. ¿Qué pasa con el operador de asignación? – LihO

+3

@LihO en general, si deshabilita uno, deshabilita el otro. Si no desactiva el operador de asignación, puede hacer esto: 'MyClass a, b; a = b; 'y si no desactiva el constructor de copia, puede hacer' MyClass b; MyClass a (b); 'Entonces, si no deshabilita ambos, puede evitar que el otro se deshabilite. –

+0

Sí, ahora lo tengo. – LihO

0

Cuando intenta implementar un patrón singleton, es perfectamente aceptable utilizar un constructor privado, ya que está destinado a ser solo una instancia dentro de sí mismo y de ninguna otra parte. Una vez invocado, el constructor no puede ser revocado. Por lo tanto, el constructor se invoca solo después de verificar si la condición singleton fue satisfactoria.

2

Es una práctica bastante común. Hay muchos ejemplos donde la copia no es apropiada.

Digamos que su objeto representa un socket abierto del lado del servidor (es decir, una conexión de red entrante); ¿Cuál sería la semántica de hacer una copia de ese objeto?

5

En general, cualquier clase que administre un recurso debe ser no copiable o tener semántica de copia especializada. Lo contrario también es cierto: cualquier clase que no sea copiable o necesite una semántica de copia especializada está administrando un recurso. "Gestionar un recurso" en la lengua de C++ en la práctica significa responsable de algo de espacio en la memoria, o de una conexión a una red o una base de datos, o un identificador a un archivo, o una transacción de deshacer, y así sucesivamente.

La gestión de recursos captura bastantes ejemplos. Estas son responsabilidades que toman una operación de prefijo, una operación de sufijo y posiblemente alguna acción intermedia. La gestión de la memoria, por ejemplo, implica adquirir un identificador de una dirección de memoria que administraremos, quizás perder el tiempo con esa memoria, y finalmente liberar el controlador (porque si amas algo, que sea gratis).

template<typename T> 
struct memory { 
    memory(T const& val = T()) : p(new T(val)) { } 
    ~memory() { delete p } 
    T& operator*() const { return *p; } 
private: 
    T* p; 
}; 

// ... 
{ 
    memory<int> m0; 
    *m0 = 3; 
    std::cout << *m0 << '\n'; 
} 

Esta clase memory es casi correcta: se adquiere automáticamente el espacio de memoria subyacente y libera de forma automática, incluso si una excepción se propaga algún tiempo después de que adquirió su recurso. Pero tenga en cuenta este escenario:

{ 
    memory<double> m1(3.14); 
    memory<double> m2(m1); // m2.p == m1.p (do you hear the bomb ticking?) 
} 

Debido a que no proporcionamos la semántica de copia especializadas para memory, el compilador proporciona su propio constructor de copia y copiar asignación. Estos hacen la cosa errónea: m2 = m1 significa m2.p = m1.p, de modo que los dos punteros apuntan a la misma dirección.Está mal porque cuando m2 sale del alcance libera su recurso como un buen objeto responsable, y cuando m1 sale del alcance también libera su recurso, ese mismo recurso m2 ya ha liberado, completando una eliminación doble - un notorio escenario de comportamiento indefinido. Además, en C++ es extremadamente fácil hacer copias de un objeto sin siquiera darse cuenta: una función toma su parámetro por valor, devuelve su parámetro por valor o toma su parámetro por referencia pero luego llama a otra función que toma (o devuelve) su parámetro por valor ... Es más fácil simplemente asumir que las cosas serán intentar copiarlas.

Todo esto para decir que cuando la razón de ser de una clase es la administración de un recurso, inmediatamente debe saber que debe manejar la copia. Debe decidir

  • que admite la copia, mientras que a decidir qué copiar significa: el intercambio seguro de los recursos, la realización de una copia profunda de los recursos que subyacen lo que no hay intercambio en absoluto, o la combinación de los dos enfoques como en copy-on-write o copia perezosa Cualquiera que sea la ruta que elija, deberá proporcionar un constructor de copia especializada y un operador de asignación de copias.
  • o no admite ningún tipo de copia del recurso, en cuyo caso desactiva el constructor de copia y el operador de asignación de copia.

Iría tan lejos y diré que la gestión de recursos es el único caso en el que se deshabilita la copia o se proporciona una semántica de copia especializada. Esta es solo otra perspectiva en The Rule of Three.

0

cuando se le permite crear una instancia de objeto solo después de marcar como en el caso de singleton necesita constructores privados. cuando se llama al constructor, se llamará a la instancia del objeto y entonces no tiene sentido comprobar si ya hay otra instancia. Entonces, lo que hacemos es llamar a una función miembro de clase desde la función principal y dentro de esa verificación de función miembro si ya hay otra instancia en la memoria. si no se llama al constructor. otro abortado marque las clases de singleton u otras clases protegidas donde los datos del objeto deben mantenerse seguros y no se debe permitir que se copien.

también comprobar esto: Singleton Class in C++

Cuestiones relacionadas