virtuales qué puras programacion polimorfismo funciones conoce como clases clase abstractas abstracta c++ copy clone virtual-method object-slicing

c++ - qué - ¿Es una buena idea poner métodos virtuales en un tipo copiable?



funciones virtuales puras en java (4)

El único ejemplo de contador que tengo son las clases que están destinadas a ser asignadas a la pila y no asignadas a la pila. Un esquema para el que lo uso es la inyección de dependencia:

class LoggerInterface { public: virtual void log() = 0; }; class FileLogger final: public LoggerInterface { ... }; int main() { FileLogger logger("log.txt"); callMethod(logger, ...); }

El punto clave aquí es la palabra clave final , sin embargo, significa que copiar un FileLogger no puede llevar a la FileLogger de objetos.

Sin embargo, puede ser que al ser final se FileLogger convertido FileLogger en una clase de valor.

Nota: lo sé, copiar un registrador parece extraño ...

He visto algunas preguntas relacionadas, pero no esta exacta ...

He tratado las clases como encajando en algunas categorías principales, digamos estas cuatro para simplificar:

  • Clases de valor que tienen algunos datos y un montón de operaciones. Se pueden copiar y comparar de manera significativa para lograr la igualdad (se espera que las copias sean iguales a través de == ). Estos casi siempre carecen de métodos virtuales.

  • Clases únicas cuyas instancias tienen una identidad que deshabilita la asignación y la copia. Normalmente no hay un operator== en estos porque los comparas como punteros, no como objetos. Estos a menudo tienen una gran cantidad de métodos virtuales, ya que no hay riesgo de object-slicing ya que se ve obligado a pasarlos por puntero o referencia.

  • Clases únicas pero clonables que inhabilitan la copia, pero que están diseñadas de antemano para admitir la clonación si eso es lo que realmente desea. Estos tienen métodos virtuales, sobre todo aquellos que siguen el lenguaje virtual de construcción / clonación.

  • Clases de contenedores que heredan las propiedades de lo que tienen. Estos tienden a no tener métodos virtuales ... ver, por ejemplo, "¿Por qué los contenedores STL no tienen destructores virtuales?" .

Independientemente de mantener este sistema de creencias informal, un par de veces he intentado agregar un método virtual a algo que pueda copiarse. Si bien pensé que sería "realmente genial si eso funcionara", inevitablemente se rompe.

Esto me llevó a preguntarme si alguien tiene un buen ejemplo real de un tipo que tenga métodos virtuales y no desactive la copia .


El despacho virtual pasa un tiempo de ejecución . La única razón por la que uno debería quererlo es cuando el tipo real y dinámico de un objeto no se puede conocer hasta el tiempo de ejecución. Si ya conocía el tipo dinámico deseado al escribir el programa, podría usar diferentes técnicas no virtuales (como plantillas o herencia no polimórfica) para estructurar su código.

Un buen ejemplo de la necesidad de escribir en tiempo de ejecución es analizar los mensajes de E / S o manejar eventos: cualquier tipo de situación en la que, de una u otra forma, tendrá algún tipo de tabla de interruptores grande para elegir el tipo concreto correcto, o escribirá su propio sistema de registro y despacho, que básicamente reinventa el polimorfismo, o simplemente utiliza el despacho virtual.

(Permítame interponer una advertencia: muchas personas hacen un uso incorrecto de las funciones virtuales para resolver problemas que no las necesitan, porque no son dinámicas. Tenga cuidado y sea crítico con lo que ve).

Dicho esto, ahora está claro que su código tratará principalmente con las clases base polimórficas, por ejemplo, en interfaces de función o en contenedores. Entonces, reformulemos la pregunta: ¿Debería ser copiable esa clase base? Bueno, ya que nunca tiene objetos base reales, más derivados (es decir, la clase base es esencialmente abstracta), esto no es realmente un problema, y ​​no hay necesidad de esto. Ya ha mencionado el modismo "clon", que es el análogo apropiado de copiar en un polimórfico.

Ahora, la función "clonar" se implementa necesariamente en cada clase de hoja, y necesariamente requiere copiar las clases de hoja. Entonces, sí, cada clase hoja en una jerarquía clonable es una clase con funciones virtuales y un constructor de copia. Y como el constructor de copia de una clase derivada necesita copiar sus subobjetos base, todas las bases también deben poder copiarse.

Entonces, ahora creo que hemos resuelto el problema en dos posibles casos: o todo en su jerarquía de clases es completamente imposible de copiar, o su jerarquía admite la clonación , y por lo tanto, por necesidad, todas las clases en ella son copiables.

Entonces, ¿debería una clase con funciones virtuales tener un constructor de copia? Absolutamente. (Esto responde a su pregunta original: cuando integra su clase en una jerarquía clonora y polimórfica, le agrega funciones virtuales).

¿Deberías tratar de hacer una copia de una referencia base? Probablemente no.


No con una sola, sino con dos clases:

#include <iostream> #include <vector> #include <stdexcept> class Polymorph { protected: class Implementation { public: virtual ~Implementation() {}; // Postcondition: The result is allocated with new. // This base class throws std::logic error. virtual Implementation* duplicate() { throw std::logic_error("Duplication not supported."); } public: virtual const char* name() = 0; }; // Precondition: self is allocated with new. Polymorph(Implementation* self) : m_self(self) {} public: Polymorph(const Polymorph& other) : m_self(other.m_self->duplicate()) {} ~Polymorph() { delete m_self; } Polymorph& operator = (Polymorph other) { swap(other); return *this; } void swap(Polymorph& other) { std::swap(m_self, other.m_self); } const char* name() { return m_self->name(); } private: Implementation* m_self; }; class A : public Polymorph { protected: class Implementation : public Polymorph::Implementation { protected: Implementation* duplicate() { return new Implementation(*this); } public: const char* name() { return "A"; } }; public: A() : Polymorph(new Implementation()) {} }; class B : public Polymorph { protected: class Implementation : public Polymorph::Implementation { protected: Implementation* duplicate() { return new Implementation(*this); } public: const char* name() { return "B"; } }; public: B() : Polymorph(new Implementation()) {} }; int main() { std::vector<Polymorph> data; data.push_back(A()); data.push_back(B()); for(auto x: data) std::cout << x.name() << std::endl; return 0; }

Nota: en este ejemplo, los objetos se copian siempre (aunque puede implementar semántica compartida)


No hay nada inherentemente malo en poder copiar una clase polimórfica. El problema es poder copiar una clase sin hojas. Recortar objetos te atrapará.

Una buena regla a seguir nunca se deriva de una clase concreta . De esta manera, las clases que no son hojas no son automáticamente creables y, por lo tanto, no se pueden copiar. Sin embargo, no estará de más deshabilitar la asignación en ellos, solo para estar en el lado seguro.

Por supuesto, no hay nada de malo en copiar un objeto a través de una función virtual. Este tipo de copia es segura.

Las clases polimórficas normalmente no son "clases de valor" pero sí sucede. std::stringstream viene a la mente. No se puede copiar, pero es móvil (en C ++ 11) y el movimiento no es diferente de copiar con respecto al corte.