2009-08-27 11 views
16

En el estándar de codificación de nuestra compañía, se nos ha dicho que "tengamos en cuenta las maneras en que se puede evitar la copia (accidental)".previene la copia accidental de objetos en C++

No estoy seguro de lo que esto significa, pero supongo que significa que debemos evitar que las clases se copien si no es necesario.

Lo que ocurre es la siguiente:

  1. Hacer el constructor copia de una clase privada.
  2. Haga que el operador de asignación (operator =) de una clase sea privado.
  3. Haga que el constructor de una clase sea explícito (para evitar que las clases se creen utilizando variables incorrectas).
  4. Para todas las clases que llevan a cabo la asignación de memoria y donde se requiere copiar, asegúrese de que el constructor de copias y el operador de asignación lleven a cabo una copia profunda en lugar de una copia poco profunda.

¿Estoy en el camino correcto? ¿Hay algo que podría haberme perdido?

+0

+1. Creo que lo tienes exactamente en tu lista :) – kkaploon

+0

En realidad, habría entendido que esa oración significa que debes ser consciente de estas cosas y ten cuidado de no copiar accidentalmente los objetos que no se supone que debas. ¿Pero que se yo? La pregunta es más interesante de esta manera, de todos modos. –

Respuesta

9

Si su estándar de codificación dice "tenga en cuenta la forma en que se puede evitar la copia (accidental)", supongo que no solo se trata de evitar copias dentro de las clases, sino también las implicaciones de rendimiento innecesarias/copias accidentales cuando usando las clases.

Una de las principales causas del rendimiento innecesariamente desperdiciado en el código de personas nuevas en C++ es la copia innecesaria, generalmente a través de temporarios. Los compiladores son cada vez mejores para decidir cuándo no son necesarios los temporales (ver "Want speed? Pass by Value", gracias al comentario de Konrad), pero lo mejor que se puede hacer es aprender a conocer el funcionamiento interno de las copias y los temporales en C++ (entre otros). Para mí, leer Efficient C++ realmente me ayudó a comenzar.

+2

Su segundo punto es en gran medida erróneo/engañoso, al tomar copia elisión y NRVO en cuenta. Para que un profesional hable: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ –

+0

De hecho, es un excelente artículo: sabía de RVO, pero la última vez que profundicé profundamente, todavía estaba usando VS2003 y era un asunto arriesgado depender del compilador para hacerlo bien para tipos más complejos. Me alegra saber que se está convirtiendo en un problema mucho menor con C++ 0X –

+0

@MadKeithV: No se deje engañar por la mención de C++ 0x: * todo * en ese artículo se aplica a los compiladores actuales y la versión actual de C++, nada es nuevo para C++ 0x. –

10

Si está utilizando impulso, entonces la manera más fácil de prevenir una clase de la copia es mediante la derivación de la clase de noncopyable:

#include <boost/noncopyable.hpp> 

class Foo : private boost::noncopyable 
{ 
} 

que hace que su intención más clara que hacer manualmente el constructor de copia y el operador assigment privado, y tiene el mismo resultado.

+0

No creo que quiera evitar que se copie la clase, sino que quiere evitar que se copie * accidentalmente *. – kkaploon

+0

Creo que sí, porque enumera como una de las opciones que hace que el constructor de copias sea privado. –

+0

Por mucho que me guste aumentar, me he encontrado con varias situaciones de trabajo en las que simplemente no estaba permitido. – Greg

21

Sí, al hacer que el operador de asignación y el constructor de copia sean privados evitará que cree cualquier copia de objeto utilizando métodos estándar (pero si realmente necesita una copia de un objeto puede implementar, por ejemplo, el método Copy(), que realizar una copia profunda).

Echa un vistazo en boost::noncopyable.

actualización (re a Tal Pressman):

... usted debe ser consciente de estas cosas y ver que no copie accidentalmente objetos que no se supone a.

Bueno, supongo, que cualquier accidental copia se lleva a cabo utilizando ya sea operador de asignación o constructor de copia. Por lo tanto, hacerlos privados realmente tiene sentido: si la copia de objetos es una operación costosa, entonces la copia debe ser explícita: otro desarrollador puede indirectamente llamar copy op y el compilador le informará que esto está prohibido.

+0

+1 para boost noncopyable –

2

Estás en el camino correcto. Si no desea utilizar el impulso, puede hacer lo siguiente: Haga que el constructor de copias y el operador de asignación de copias sean privados y no los implemente. Por lo tanto, obtendrá un error de compilación si intenta copiar una instancia.

0

Su lista se ve muy bien desde el punto de vista de evitar errores, p. eliminar la misma área de memoria más de una vez debido a punteros compartidos de copia implícita de objetos.

Espero que esto también sea relevante; del enunciado "tenga en cuenta las maneras en que se puede evitar el copiado (accidental)", puede interpretarlo como "tenga en cuenta la copia no intencional involuntaria". Esto significaría circunstancias que podrían afectar el rendimiento. En un ejemplo muy simple de sus convenciones de codificación podría significar que usted debe preferir:

std::string text1("some text"); 

sobre: ​​

std::string text1 = "some text"; 

El segundo caso se traduciría en una cadena temporal que se está creando para sostener "un texto" antes de la asignación se invoca al operador (una forma de copia) para llenar texto1 con "texto". Obviamente, este es un ejemplo trivial y las buenas prácticas dictarían que debe usar la inicialización del constructor (primer ejemplo) siempre que sea posible.

+0

Gracias por tu comentario. Esto es algo de lo que no estoy enterado. Por lo tanto, siempre llame a un constructor en lugar de utilizar una asignación, si se requiere una conversión de tipo. – Andy

+0

No estoy seguro de que estés en lo correcto con esta. Ambos crean un char temporal []. –

+1

No, eso es simplemente incorrecto. * Todos los compiladores modernos evitan la instancia temporal innecesaria de 'std :: string' en este caso. –

0

Sí, le falta algo, puede haber un caso en una clase donde una función miembro está copiando un objeto a otro. Una función de miembro puede acceder al constructor de copia privada o al operador de asignación.

La mejor manera de hacer otra clase .... digamos NonCopyable, esta clase tendrá su constructor de copia y operador de asignación como privado, luego haga que su clase digamos MyClass una subclase de la clase NonCopyable.

Esto restringirá la creación de cualquier objeto de Myclass, ya que requerirá una llamada del constructor de clase NonCopyable. Además, dado que la función miembro de Myclass no tendrá acceso a los constructores privados de la clase NonCopyable, también restringe esta situación.

También será más elegante hacer que un objeto de clase no se pueda copiar heredando una clase No Aceptable.

Cuestiones relacionadas