Desde un constructor de copia

MyClass(const MyClass&);

y = sobrecarga de operadores

MyClass& operator = (const MyClass&);

tienen casi el mismo código, el mismo parámetro, y sólo se diferencian en el retorno, es posible tener una función común para ambos a utilizar?

  • «…tienen casi el mismo código…»? Hmm… debes estar haciendo algo mal. Trate de minimizar la necesidad de utilizar funciones definidas por el usuario para el presente y dejar que el compilador haga todo el trabajo sucio. Esto a menudo significa la encapsulación de los recursos en su propio objeto. Usted puede mostrarnos algo de código. Tal vez tenemos algunas buenas sugerencias de diseño.
  • Posibles duplicados de Reducir la duplicación de código entre el operador= y el constructor de copia
InformationsquelleAutor MPelletier | 2009-11-14

3 Comentarios

  1. 109

    Sí. Hay dos opciones comunes. – Que generalmente se desaconseja – es llamar a la operator= desde el constructor de copia explícitamente:

    MyClass(const MyClass& other)
    {
        operator=(other);
    }

    Sin embargo, proporcionar una buena operator= es un reto cuando se trata de lidiar con el viejo estado y de las cuestiones derivadas del auto de asignación. También, todos los miembros y bases de obtener defecto inicializa primero, incluso si van a ser asignados a partir de other. Esto puede incluso no ser válida para todos los miembros y las bases y hasta donde es válido es semánticamente redundante y puede ser prácticamente caro.

    Una solución cada vez más popular es implementar operator= utilizando el constructor de copia y un método de intercambio.

    MyClass& operator=(const MyClass& other)
    {
        MyClass tmp(other);
        swap(tmp);
        return *this;
    }

    o incluso:

    MyClass& operator=(MyClass other)
    {
        swap(other);
        return *this;
    }

    Un swap función normalmente es sencillo escribir como se acaba de swaps de la propiedad de la información interna y no tener que limpiar estado existente o asignar nuevos recursos.

    Ventajas de la copia y el intercambio modismo es que automáticamente se auto-asignación de seguro y siempre que la operación de intercambio es no-tiro – también está fuertemente excepción segura.

    A ser fuertemente excepción caja fuerte, una «mano» escritas operador de asignación normalmente tiene que asignar una copia de los nuevos recursos antes de la asignación al cesionario antiguo de recursos, de forma que si se produce una excepción la asignación de los nuevos recursos, el viejo estado todavía puede ser devuelto. Todo esto viene gratis con la copia-y-swap, pero normalmente es más compleja, y por lo tanto propenso al error, a hacer desde cero.

    La única cosa a tener cuidado es para asegurarse de que el método de intercambio es un verdadero intercambio, y no el valor predeterminado std::swap que utiliza el constructor de copia y el operador de asignación en sí.

    Normalmente un memberwise swap se utiliza. std::swap funciona y es de «no tirar» garantizado con todos los tipos básicos y los tipos de puntero. La mayoría de los punteros inteligentes también pueden ser intercambiados con un no-tirar de garantía.

    • En realidad, no son operaciones comunes. Mientras que la copia cto r primera vez que se inicializa el objeto de miembros, el operador de asignación reemplaza los valores existentes. Teniendo en cuenta esto, alling operator= de la copia de la cto r es, de hecho, bastante malo, ya que primero se inicializa todos los valores a algún defecto sólo para reemplazar con el resto del objeto de los valores a la derecha después.
    • Me dijo que las opciones comunes, no las operaciones. Estoy totalmente de acuerdo en que la vocación operator= de un constructor de copia no es buena, pero usted sólo tiene que mirar a un razonable acerca del mundo real código para ver de lo común que es.
    • Downvoters, cuidado de explicar?
    • Um, no acaba de responder a mi explicación?
    • Tal vez «no lo recomiendo», agregar «y ninguna de C++ experto». Alguien podría venir y no se dan cuenta de que no son la simple expresión de personal de la minoría de preferencia, pero la reiterada opinión de consenso de los que en realidad he pensado en ello. Y, OK, tal vez estoy equivocado y un poco de C++ experto no lo recomiendo, pero personalmente creo que todavía me echó el guante para que alguien venga con una referencia a esa recomendación.
    • Sí, respondí a tu comentario. En mi humilde opinión, ambas opciones son muy comunes, pero usted no está de acuerdo. La popularidad es en cierta medida una cuestión de opinión, así que estoy feliz de estar en desacuerdo en ese punto, pero estoy interesado en saber por qué he sido votada abajo, que debe significar «mal o ineficiente», que no era mi intención.
    • Jessop: no me gusta el sonido demasiado prescriptiva, la gente a usar C++ felizmente (o no tan felizmente) de muchas maneras diferentes. He añadido el «yo no recomiendo’ después de volver a leer mi respuesta, porque sentí que mi desaprobación probablemente no se muestra lo suficientemente fuerte por solo el contenido del siguiente párrafo.
    • Justo lo suficiente, me upvoted ya que de todos modos :-). Me imagino que si algo es ampliamente considerado como la mejor práctica, entonces es mejor decirlo así (y mire de nuevo si alguien dice que no es realmente la mejor manera, después de todo). Del mismo modo, si a alguien le preguntó «¿es posible el uso de mutexes, en C++», yo no diría «uno bastante común opción es ignorar completamente RAII, y escribir no-excepción-código seguro de que los interbloqueos en la producción, pero es cada vez más popular para escribir decente, código de trabajo» 😉
    • +1. Y creo que siempre hay análisis en necesidad. Creo que es razonable tener un assign miembro de la función que se utiliza por tanto la copia cto r y el operador de asignación en algunos casos (por ligero de clases). En otros casos (recursos/casos de uso, handle/cuerpo) una copia/swap es el camino a seguir, por supuesto.
    • De acuerdo, por ejemplo, si usted acaba de conseguir un par de tipos básicos en una clase y la asignación de sus miembros no cuentan siquiera con la auto-asignación de excepción o de seguros de problemas, en cualquier caso, por lo que hay poco punto en la sudoración acerca de la solución más robusta. Tan pronto como su constructores new o hacer cualquier tipo de asignación de recursos de empezar a considerar sus opciones un poco más cuidado.
    • Si la clase es de peso ligero, hace copia de canje de hacer ningún daño?
    • Sí, desde luego usted no necesita de un especial de intercambio, y no veo lo que usted compra wrt excepción de seguridad. Simplemente añade un inútil de la función (función de miembro intercambio), y crear una inútil copiar (ya que el swap/copia sería esencialmente la misma velocidad aquí, es inútil en el sentido real). Pero yo podría estar equivocado, por supuesto, estoy simplemente no ver el punto en hacer copia de intercambio de entonces.
    • Por supuesto, depende de qué es exactamente «ligera» es – me refiero a una clase que no es la memoria dinámica de la gestión o explotación de los recursos. La necesidad de que el usuario define cctor o asignación de la ops en este caso es raro, creo yo – pero de vez en cuando puede venir para arriba.
    • No va a downvote, pero nunca he visto a un constructor de copia implementadas en términos de operator= y la verdad es que parece ineficiente.
    • Les puedo asegurar que es deprimente común. Nunca he visto en el código que de otra manera me han hecho pensar: ‘Esto es realmente limpio c++, aquí!’, sin embargo.
    • De hecho, déjame citar Herb Sutter: «en todo Caso, el lenguaje es exactamente al revés: copia de la construcción debe ser implementado en términos de copia de asignación, en lugar de a la inversa.» -> gotw.ca/gotw/023.htm . Por supuesto que el artículo es viejo, pero esta muestra no es del todo loco de un pensamiento 🙂
    • Perdóname si mi falta de conocimiento que se muestra, pero, ¿cómo, en el intercambio de la solución, es la inicial de la destrucción del objetivo asegurado?
    • Yo estaba sorprendido por esto por lo que me miró Artículo 41 de la Excepción de C++ (que este gotw convertido en) y esta recomendación se ha ido y él recomienda copiar-y-swap en su lugar. En lugar sneakily él ha caído «Problema #4: no Es eficiente para la Asignación» al mismo tiempo.
    • El swap de acción mueve el antiguo estado de la meta en la copia temporal (como la copia del estado se trasladó a la de destino). Como la copia inicial es local para el operador de asignación se sale del ámbito en el extremo del cuerpo de la función, asegurando así que el viejo estado de la meta es detroyed.
    • ah, que bueno. Yo no esperaba otra cosa de él, ahora sé que realmente hizo «fix» que 🙂 no obstante, no creo que sobre todo es «malo» para llamar a una función común de ambos, el cctor y copia operador de asignación, como algunas personas aquí. Por qué hacerlo de la manera compleja si una solución fácil es suficiente.
    • +1 Sutter también cubre copia de intercambio en un posterior gotw: gotw.ca/gotw/059.htm
    • En realidad, que exactamente ¿cómo T a(b); fue definido waaay de la espalda: en la primera llamada al constructor predeterminado y, a continuación, llamar el operador de asignación. Desde esta fue encontrado para ser muy ineficientes, la noción de un «constructor de copia» fue introducido en C++ 🙂
    • Tengo una preocupación acerca de la firma Miclase& operador=(Miclase otros). El Visual C++ 2013 compilador parece aún generar automáticamente un Miclase& operator=(const Miclase& otros) función de la aplicación de memberwise asignación. Si el Miclase& operador=(Miclase otros) de la firma, ha suprimido la VS generado?
    • No es la primera sugerencia, th enot recomienda un ilegal? Si el operador = se elimina de la memoria, esto eliminará al azar (no inicializado) puntero. Creo que no es simplemente «no se recomienda» pero se garantiza accidente
    • la memoria»? Lo de la memoria? Suena como que usted tiene un ejemplo específico en mente. Si operator= está escrito correctamente no debería desencadenar un accidente.
    • el operador = se supone asumir que el objeto ya existe. Lo que si existe es un puntero, el objeto debe ser eliminado. Constructor de copia no. La segunda solución, donde el operador = se escribe en términos de la copia constructor de sentido. Pero la primera, ¿cómo se escribe si el objeto contiene una asignación dinámica de puntero?
    • ¿Hay alguna restricción para escribir MyClass(other).swap(*this); o this->swap(MyClass(other)); ?

  2. 12

    El constructor de copia se realiza por primera vez la inicialización de los objetos que utiliza para ser cruda de la memoria. El operador de asignación, OTOH, reemplaza a los valores existentes con los nuevos. Más a menudo que nunca, esto implica desestimar viejos recursos (por ejemplo, la memoria) y la asignación de los nuevos.

    Si hay una similitud entre los dos, es que el operador de asignación se realiza la destrucción y la copia de la construcción. Algunos desarrolladores utilizan para implementar realmente asignación en lugar de la destrucción seguido por la colocación de copia de la construcción. Sin embargo, este es un muy mala idea. (Lo que si este es el operador de asignación de una clase base que se llama durante la asignación de una clase derivada?)

    Lo que usualmente se considera canónica de la jerga de hoy en día es el uso de swap como Charles sugerido:

    MyClass& operator=(MyClass other)
    {
        swap(other);
        return *this;
    }

    Este utiliza la copia de la construcción (tenga en cuenta que other es copiado) y la destrucción (es destruido al final de la función) — y se utiliza en el orden correcto, demasiado: construcción (puede fallar) antes de la destrucción (que no falle).

    • Debe swap ser declarado virtual?
    • Las funciones virtuales son utilizados en polimórficos jerarquías de clase. Operadores de asignación se utilizan los tipos de valor. Los dos apenas la mezcla.
  3. -3

    Algo que me molesta sobre:

    MyClass& operator=(const MyClass& other)
    {
        MyClass tmp(other);
        swap(tmp);
        return *this;
    }

    Primer lugar, la lectura de la palabra «intercambio» cuando mi mente está pensando en «copiar» irrita mi sentido común. También, me pregunta el objetivo de esta fantasía truco. Sí, las excepciones en la construcción de la nueva (copiados) los recursos deben suceder antes de que el swap, que parece ser una forma segura para asegurarse de que todos los nuevos datos se llenan antes de lo que es ir a vivir.

    Que está bien. Entonces, ¿qué acerca de las excepciones que ocurren después de la swap? (cuando el viejo recursos son destruidas cuando el temporal objeto queda fuera del ámbito de aplicación) Desde la perspectiva del usuario de la asignación, la operación ha fallado, excepto que no. Tiene un enorme efecto secundario: la copia se hizo realidad. Fue sólo algunos de los recursos de la limpieza que no se pudo. El estado del objeto de destino ha sido alterada, incluso aunque la operación parece desde el exterior, a los que han fallado.

    Así, yo propongo que en lugar de «swap» más natural «transferencia»:

    MyClass& operator=(const MyClass& other)
    {
        MyClass tmp(other);
        transfer(tmp);
        return *this;
    }

    Todavía hay la construcción del objeto temporal, pero la próxima acción inmediata es liberar todos los recursos actuales de la de destino antes de la mudanza (y el Anular de manera que no se doble-librado) de los recursos de la fuente.

    Lugar de { construir, mover, destruir }, propongo { construir, destruir, mover }. La medida, que es el más peligroso de la acción, es el que se toma la última después de todo lo demás ha sido resuelto.

    Sí, la destrucción no es un problema en uno y otro régimen. Los datos está dañado (copiado siempre y cuando, no creo que era) o perdido (libera cuando no creo que fue). Perdido es mejor que el dañado. Los datos No es mejor que un mal de datos.

    De transferencia en lugar de swap. Esa es mi sugerencia de todos modos.

    • Un destructor no debe fallar, por lo que las excepciones a la destrucción no son de esperar. Y, no entiendo cuál sería la ventaja de mover el movimiento detrás de la destrucción, si se mueven es el más peligroso de la operación? I. e., en el esquema de la norma, un movimiento fallo no se va a corromper el estado anterior, mientras que su nuevo sistema hace. Entonces, ¿por qué? También, First, reading the word "swap" when my mind is thinking "copy" irritates -> Como una biblioteca escritor, que sabe prácticas comunes (copy+swap), y el quid es my mind. Tu mente es en realidad oculta detrás de la interfaz pública. Eso es lo que re-utilizable código es todo acerca de.

Dejar respuesta

Please enter your comment!
Please enter your name here