que otro llenar copiar como arreglo array c++ clone smart-pointers covariant-return-types

otro - ¿Cuál es la mejor firma para clonar() en C++?



copiar un arreglo a otro en c (8)

Como escribió Scott Myers, puede aprovechar una relajación en el sistema de tipos de C ++ para declarar que clone () devuelve un puntero al tipo real que se está declarando:

class Base { virtual Base* clone() const = 0; }; class Derived : public Base { virtual Derived* clone() const };

El compilador detecta que clone () devuelve un puntero al tipo del objeto, y permite que Derived lo anule para devolver un puntero a derivado.

Sería deseable que clone () devuelva un puntero inteligente que implique una semántica de transferencia de propiedad, como la siguiente:

class Base { virtual std::auto_ptr<Base> clone() const = 0; }; class Derived : public Base { virtual std::auto_ptr<Derived> clone() const; };

Desafortunadamente, la relajación de las convenciones no se aplica a los punteros inteligentes con plantillas, y el compilador no permitirá la anulación.

Entonces, parece que me quedan dos opciones:

  1. Haga que clone () devuelva un puntero "tonto" y documente que los clientes son responsables de deshacerse de él.
  2. Haga que clone () devuelva un puntero base inteligente, y haga que los clientes usen dynamic_cast para guardarlos en un puntero Derivado si lo necesitan.

¿Se prefiere uno de estos enfoques? ¿O hay una manera para que yo pueda comer mi semántica de transferencia de propiedad y tener mi fuerte seguridad también?


Creo que la semántica de la función es tan clara en este caso que hay poco espacio para la confusión. Así que creo que puede usar la versión covariante (la que devuelve un puntero tonto al tipo real) con una conciencia tranquila, y las personas que llaman sabrán que están recibiendo un nuevo objeto cuya propiedad se les transfiere.


Depende de tu caso de uso. Si alguna vez piensa que necesitará llamar al clone en un objeto derivado cuyo tipo dinámico conozca (recuerde que el objetivo del clone es permitir la copia sin conocer el tipo dinámico), entonces probablemente debería devolver un puntero tonto y cargarlo en un puntero inteligente en el código de llamada. De lo contrario, solo debe devolver un smart_ptr y, por lo tanto, puede devolverlo en todas las anulaciones.


Esa es una de las razones para usar boost::intrusive_ptr lugar de shared_ptr o auto/unique_ptr . El puntero sin formato contiene el recuento de referencias y se puede usar de forma más uniforme en situaciones como esta.


La sintaxis no es tan buena, pero si agrega esto a su código anterior, ¿no soluciona todos sus problemas?

template <typename T> std::auto_ptr<T> clone(T const* t) { return t->clone(); }


Puede tener dos métodos, un clon virtual () que devuelve un contenedor de puntero inteligente alrededor del tipo base y un clone2 () no virtual que devuelve el tipo correcto de puntero inteligente.

clone2 obviamente se implementaría en términos de clonar y encapsular el elenco.

De esa manera puede obtener el puntero inteligente más derivado que conoce en tiempo de compilación. Puede que no sea el tipo más derivado en general, pero utiliza toda la información disponible para el compilador.

Otra opción sería crear una versión de plantilla de clon que acepte el tipo que está esperando, pero que agrega más carga a la persona que llama.


Utilice el patrón virtual Público no virtual / privado:

class Base { public: std::auto_ptr<Base> clone () { return doClone(); } private: virtual Base* doClone() { return new (*this); } }; class Derived : public Base { public: std::auto_ptr<Derived> clone () { return doClone(); } private: virtual Derived* doClone() { return new (*this); } };


Tr1::shared_ptr<> puede convertir como si fuera un puntero sin formato.

Creo que tener clone () devolver un shared_ptr<Base> es una solución bastante limpia. Puede convertir el puntero en shared_ptr<Derived> mediante tr1::static_pointer_cast<Derived> o tr1::dynamic_pointer_cast<Derived> en caso de que no sea posible determinar el tipo de objeto clonado en tiempo de compilación.

Para asegurarse de que el tipo de objeto sea predecible, puede usar un molde polimórfico para shared_ptr como este:

template <typename R, typename T> inline std::tr1::shared_ptr<R> polymorphic_pointer_downcast(T &p) { assert( std::tr1::dynamic_pointer_cast<R>(p) ); return std::tr1::static_pointer_cast<R>(p); }

La sobrecarga agregada por el afirmar será descartada en la versión de lanzamiento.


Actualizando la respuesta de MSalters para C ++ 14:

#include <memory> class Base { public: std::unique_ptr<Base> clone() const { return do_clone(); } private: virtual std::unique_ptr<Base> do_clone() const { return std::make_unique<Base>(*this); } }; class Derived : public Base { private: virtual std::unique_ptr<Base> do_clone() const override { return std::make_unique<Derived>(*this); } }