Quiero realizar "copias en profundidad" de un contenedor STL de punteros a clases polimórficas.C++ Virtual Constructor, sin clon()
lo que sé sobre el patrón de diseñoPrototipo, implementado por medio de la virtual Ctor Idiom, como se explica en el C++ FAQ Lite, Item 20.8.
Es simple y directo:
struct ABC // Abstract Base Class
{
virtual ~ABC() {}
virtual ABC * clone() = 0;
};
struct D1 : public ABC
{
virtual D1 * clone() { return new D1(*this); } // Covariant Return Type
};
una copia profunda es entonces:
for(i = 0; i < oldVector.size(); ++i)
newVector.push_back(oldVector[i]->clone());
Inconvenientes
Como Andrei Alexandrescu states it:
La aplicación
clone()
debe seguir el mismo patrón en todos los clas derivados ses; a pesar de su estructura repetitiva, no hay una forma razonable de automatizar la definición de la función de miembroclone()
(más allá de las macros, eso es).
Por otra parte, los clientes de ABC
pueden posiblemente hacer algo malo. (Es decir, nada impide a los clientes a hacer algo malo, así, se suceder.)
mejor diseño?
Mi pregunta es: ¿hay alguna otra forma de hacer una clase base abstracta clonable sin requerir que las clases derivadas escriban código relacionado con el clon? (Clase auxiliar? Plantillas?)
siguiente es mi contexto. Con suerte, ayudará a entender mi pregunta.
Estoy diseñando una jerarquía de clases para realizar operaciones en una clase Image
:
struct ImgOp
{
virtual ~ImgOp() {}
bool run(Image &) = 0;
};
operaciones de imagen son definidos por el usuario: los clientes de la jerarquía de clases va a poner en práctica sus propias clases derivan de ImgOp
:
struct CheckImageSize : public ImgOp
{
std::size_t w, h;
bool run(Image &i) { return w==i.width() && h==i.height(); }
};
struct CheckImageResolution { ... };
struct RotateImage { ... };
...
operaciones múltiples pueden ser realizadas secuencialmente en una imagen:
bool do_operations(vector< ImgOp* > v, Image &i)
{
for_each(v.begin(), v.end(),
/* bind2nd(mem_fun(&ImgOp::run), i ...) don't remember syntax */);
}
Si hay varias imágenes, el conjunto se puede dividir y compartir en varios subprocesos. Para garantizar la seguridad del hilo, cada hilo debe tener su propia copia de todos los objetos de operación contenidos en v
- v
se convierte en un prototipo para ser copiado profundamente en cada hilo.
Editado: La versión flujos seguros utiliza el patrón de diseño de prototipos para hacer cumplir copia de punta-a-objetos - PAD: no
struct ImgOp
{
virtual ~ImgOp() {}
bool run(Image &) = 0;
virtual ImgOp * clone() = 0; // virtual ctor
};
struct CheckImageSize : public ImgOp { /* no clone code */ };
struct CheckImageResolution : public ImgOp { /* no clone code */ };
struct RotateImage : public ImgOp { /* no clone code */ };
bool do_operations(vector< ImgOp* > v, Image &i)
{
// In another thread
vector< ImgOp* > v2;
transform(v.begin(), v.end(), // Copy pointed-to-
back_inserter(v2), mem_fun(&ImgOp::clone)); // objects
for_each(v.begin(), v.end(),
/* bind2nd(mem_fun(&ImgOp::run), i ...) don't remember syntax */);
}
Esto tiene sentido cuando las clases de operación de imagen son pequeños : no serialice accesos a instancias únicas de ImgOp
s, en lugar de proporcionar a cada subproceso sus propias copias.
La parte difícil es evitar que los escritores de las nuevas ImgOp
-derivadas clases escriban cualquier código relacionado con el clon. (Debido a que este es el detalle de la implementación, esta es la razón por la que descarté las respuestas de Paul con el patrón curiosamente recurrente).
Yo diría que, de hecho, es posible automatizar 'clone()' (si es un poco poco elegante): http://nerdland.net/2009/06/covariant-templatized-virtual-copy-constructors/ –
@tyler McHenry : Pero hacerlo automático alentaría el mal uso y luego C++ comenzaría a parecerse a Java. Yo diría que casi todos los usos de clone() son incorrectos (en los pocos casos en los que es necesario permitir que la gente haga el trabajo de implementarlo) desafortunadamente es fácil de usar de manera incorrecta lo que lleva a un mal diseño e implementación. –
¿Qué hay de malo con solo definir y usar el constructor de copia de la manera en que debería hacerlo? Como dice @Martin, definitivamente no quieres que los objetos sean clonables arbitrariamente. – jalf