2009-09-21 5 views
31

Para no permitir copiar o asignar una clase, es práctica común hacer que el constructor de copia y el operador de asignación sean privados. Tanto Google como Qt tienen macros para que esto sea fácil y visible. Estas macros son:Macros para no permitir la copia y asignación de clase. Google -vs- Qt

Google:

#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ 
    TypeName(const TypeName&); \ 
    void operator=(const TypeName&) 

Qt:

#define Q_DISABLE_COPY(Class) \ 
    Class(const Class &); \  
    Class &operator=(const Class &); 

Preguntas: ¿Por qué las firmas de los dos operadores de asignación diferente? Parece que la versión de Qt es correcta. ¿Cuál es la diferencia práctica entre los dos?

+2

Otra diferencia mínima es que la adaptación de Qt termina con un punto y coma. – Dennis

Respuesta

5

No hay diferencia práctica. Las firmas del operador de asignación difieren solo por cuestión de estilo. Es habitual que un operador de asignación devuelva una referencia para permitir el encadenamiento:

a = b = c;

pero una versión que devuelve void también es legal y funcionará muy bien para casos en los que el único propósito es simplemente declarar el operador private y, por lo tanto, prohibido su uso.

1

Ambos tienen el mismo propósito

Una vez que escribir esto:

Class &operator=(const Class &); 

obtendrá los beneficios de asignaciones de cadena. Pero en este caso, desea que el operador de asignación sea privado. entonces no importa.

41

No importa. El tipo de devolución no es parte de la firma de una función, ya que no participa en la resolución de sobrecarga. Por lo tanto, cuando intenta realizar una tarea, ambas declaraciones coincidirán, independientemente de si usa el tipo de devolución.

Y como todo el punto en estas macros es que las funciones nunca se llamarán, no importa que uno devuelva void.

+0

Eso es lo que pensé ... gracias. Fue un poco inusual ver la versión de Google, ya que su firma no es lo que suele ver. – RobertL

2

Otros ya han respondido por qué es legal tener valores de devolución diferentes para operator =; IMHO jalf said it best.

Sin embargo, puede que se pregunte por qué Google utiliza un tipo de retorno diferente, y sospecho que es esto:

Usted no tiene que repetir el nombre del tipo de la hora de desactivar el operador de asignación como esta. Por lo general, el nombre del tipo es la parte más larga de la declaración.

Por supuesto, esta razón es nula dado que se usa una macro pero aún así, los viejos hábitos se vuelven difíciles. :-)

1

La versión de Qt es compatible hacia atrás, mientras que la de Google no lo es.

Si desarrolla su biblioteca y desaprueba el uso de la asignación antes de eliminarla por completo, en Qt probablemente conservará la firma que originalmente tenía. En este caso, la aplicación anterior continuará ejecutándose con la nueva versión de la biblioteca (sin embargo, no compilarán con la versión más nueva).

macro de Google no tiene esa propiedad.

9

Consulte Boost.Utility, específicamente boost::noncopyable. No es una macro, sino una clase base con copia y asignación privada. Impide que el compilador genere copias y asignaciones implícitas en las clases derivadas.

corregir: Disculpe, esta no era una respuesta a la pregunta original. Por cierto, boost :: noncopyable usa una referencia constante como tipo de retorno para el operador de asignación. Tenía la impresión de que el tipo de valor devuelto no importa, ya que no se supone que deba usarse. Aún así, hacer que el operador sea privado no impide el uso dentro de la clase o amigos, en cuyo caso un tipo de devolución no habitual (como void, una referencia constante, etc.) puede provocar errores de compilación y detectar errores adicionales.

15

sólo me gustaría mencionar que existe una estrategia alternativa para la implementación de una abstracción para no permitir la copia y la asignación de una clase. La idea es usar herencia en lugar del preprocesador. Yo personalmente prefiero este enfoque ya que sigo la regla general de que es mejor evitar el uso del preprocesador cuando sea posible.

boost::noncopyable es una implementación de ejemplo. Se utiliza de la siguiente manera:

class A : noncopyable 
{ 
    ... 
}; 
+0

+1 Según entiendo de esta manera, siempre obtendrá un error de compilación, mientras que al declararlos como privados en la misma clase solo podría ocasionar un error de enlazador (ya que estos no están implementados) en algunos casos (amigos, uso interno). – UncleBens

+3

Declararlos como privados dará un error de compilación – RobertL

+0

@RobertL, el punto es que todavía se puede hacer 'struct X {NON_COPYABLE (X); void f() {X x, y (x); }}; 'y solo aparece un error al momento del enlace. Heredar de noncopyable no permitirá también eso. –

0

Por cierto, si usted tiene acceso a las bibliotecas Boost (Usted no lo hace ¿Por qué diablos no ???), La biblioteca de utilidades ha tenido la noncopyable class durante mucho tiempo:

class YourNonCopyableClass : boost::noncopyable { 

mi humilde opinión más clara.

4

de la norma, 12,8, cláusula 9: "Una copia operador de asignación-declarado usuario X::operator= es una función miembro no molde no estático de la clase X con exactamente un parámetro de tipo X, X&, const X&, volátil X& o const volátil X&. " No dice nada sobre el tipo de devolución, por lo que cualquier tipo de devolución es permisible.

cláusula 10 dice: "Si la definición de clase no declara explícitamente un operador de asignación de copia, uno se declara implícitamente."

Por lo tanto, se declara cualquier X::operator=(const X&) (o cualquier otro de los tipos de asignación especificados) es suficiente. Ni el cuerpo ni el tipo de retorno son significativos si el operador nunca será utilizado.

Por lo tanto, es una diferencia estilística, con una macro haciendo lo que es probable que esperaríamos y uno ahorrar unos cuantos caracteres y hacer el trabajo de una manera que es probable que sorprender a algunas personas. Creo que la macro de Qt es mejor estilísticamente. Como estamos hablando de macro, no estamos hablando de que el programador tenga que escribir nada extra, y no sorprender a las personas es algo bueno en una construcción de lenguaje.

1

Como varias otras respuestas han mencionado, el tipo de retorno de la función no participa en la firma de la función, por lo que ambas declaraciones son equivalentes en cuanto a hacer el operador de asignación inutilizable por los clientes de la clase.

Personalmente prefiero la expresión idiomática de tener una clase heredada de forma privada de una clase base vacía no copiable (como boost :: noncopyable, pero tengo la mía para poder usarla en proyectos que no tienen boost disponible) . La optimización de la clase base vacía se ocupa de asegurarse de que no haya gastos generales, y es simple, legible y no depende de la temida funcionalidad macro del preprocesador. También tiene la ventaja de que la copia y asignación ni siquiera se pueden usar dentro del código de implementación de clase: fallará en tiempo de compilación mientras estas macros fallarán en el tiempo del enlace (probablemente con un mensaje de error menos informativo).

Cuestiones relacionadas