Muchas veces es una buena idea tener una clase base abstracta para aislar la interfaz del objeto.
El problema es que la construcción de copia, en mi humilde opinión, está bastante rota por defecto en C ++, y los constructores de copia se generan de forma predeterminada.
Entonces, ¿cuáles son las trampas cuando tienes una clase base abstracta y punteros sin procesar en clases derivadas?
class IAbstract
{
~IAbstract() = 0;
}
class Derived : public IAbstract
{
char *theProblem;
...
}
IAbstract *a1 = new Derived();
IAbstract a2 = *a1;//???
¿Y ahora deshabilita limpiamente la construcción de copia para toda la jerarquía? ¿Declarar copia de construcción como privada en IAbstract
?
¿Hay alguna regla de tres con clases base abstractas?
c++
abstract-class
Descifrador
fuente
fuente
Respuestas:
La construcción de copias en una clase abstracta debe hacerse privada en la mayoría de los casos, así como el operador de asignación.
Las clases abstractas, por definición, están hechas para ser de tipo polimórfico. Por lo tanto, no sabe cuánta memoria está usando su instancia y, por lo tanto, no puede copiarla ni asignarla de manera segura. En la práctica, corre el riesgo de cortar: /programming/274626/what-is-the-slicing-problem-in-c
El tipo polimórfico, en C ++, no debe manipularse por valor. Los manipula por referencia o por puntero (o cualquier puntero inteligente).
Esta es la razón por la cual Java ha hecho que los objetos sean manipulables solo por referencia, y por qué C # y D tienen la separación entre clases y estructuras (la primera es polimórfica y tipo de referencia, la segunda es no polimórfica y tipo de valor).
fuente
Por supuesto, podría protegerlo y vaciarlo, para que las clases derivadas puedan elegir. Sin embargo, de manera más general, su código está prohibido de todos modos porque es imposible crear una instancia
IAbstract
, ya que tiene una función virtual pura. Como tal, esto generalmente no es un problema: sus clases de interfaz no pueden instanciarse y, por lo tanto, nunca pueden copiarse, y sus clases más derivadas pueden prohibir o seguir copiando como lo deseen.fuente
operator=(const Derived2&)
enDerived1
.Al hacer que ctor y la asignación sean privados (o al declararlos como = eliminar en C ++ 11), deshabilita la copia.
El punto aquí es DONDE tienes que hacer eso. Para permanecer con su código, IAbstract no es un problema. (tenga en cuenta que al hacer lo que hizo, asigna el
*a1
IAbstract
subobjeto a a2, perdiendo cualquier referencia aDerived
. La asignación de valor no es polimórfica)El problema viene con
Derived::theproblem
. Copiar un Derivado en otro puede, de hecho, compartir los*theproblem
datos que pueden no estar diseñados para ser compartidos (hay dos instancias que pueden llamardelete theproblem
a su destructor).Si ese es el caso, es
Derived
que debe ser no copiable y no asignable. Por supuesto, si hace que la copia sea privadaIAbstract
, ya que la copia predeterminada laDerived
necesita,Derived
tampoco será copiable. Pero si define el suyoDerived::Derived(const Derived&)
sin llamar aIAbtract
copy, aún puede copiarlos.El problema está en Derived, y la solución debe permanecer en Derived: si debe ser un objeto dinámico solo al que se accede solo mediante punteros o referencias, es Derived en sí mismo el que debe tener
Básicamente, depende del diseñador de la clase Derived (que debe saber cómo funciona Derived y cómo
theproblem
se gestiona) decidir qué hacer con la asignación y la copia.fuente