puras - polimorfismo puro c++
Obtenga el tipo derivado a través de la función virtual de la clase base (5)
Encontré una solución simple, pero si es posible, los maestros evaluarían:
class base{
type = 1;
virtual int getType() final {
return type;
}
}
class derived1 : public base {
derived1(){
type = 2;
}
}
De esta forma, puede llamar al método ''int getType ()'' de cualquiera de las clases derivadas. Como el tipo se establece en el constructor, no hay riesgo de mala conducta. Para mejorar la usabilidad, he creado un ''tipo'' predefinido.
Estoy usando, pero no sé si es MacGyvery!
Estoy tratando de obtener el tipo derivado de un objeto a través de una función virtual de clase base. He escrito esto, que no compila:
struct base {
virtual base& get_this() {
return *this;
}
};
struct derived : base {
virtual derived& get_this() override {
return *this;
}
void fn();
};
int main () {
base* pd = new derived();
derived& x = pd->get_this(); /*ERROR*/
x.fn();
return 0;
}
... dándome un error que dice: no puedo inicializar un derived&
desde una base
. Como get_this
es virtual, ¿por qué pd->get_this()
devuelve una base&
lugar de un derived&
? ¡Gracias por adelantado!
EDITAR:
Gracias a todos por sus útiles respuestas y mis disculpas por mi respuesta tardía. Debería haber especificado en la publicación original que también estoy interesado en una solución a mi problema en lugar de solo averiguar por qué lo anterior no se compila. Mi problema principal es que fn
es exclusivo de la clase derived
y no se puede invocar a través de la clase base. El uso de moldes resuelve el problema, pero odio escribir código con if else construye solo para obtener el tipo correcto (también Scott Meyers desaconseja el uso de moldes :)). Las respuestas parecen indicar que los moldes son el camino a seguir, lo que de alguna manera al menos es tranquilizador de que no estoy descuidando una solución más ''elegante'' a mi problema. ¡Gracias de nuevo!
C ++ admite el tipo de retorno covariante. Lo que significa es que cuando llamas a get_this()
en un objeto derived
través de un puntero base
, es la implementación de derivada a la que se llamará.
Sin embargo, esto no significa que llamar a base::get_this
le dará un derived&
. El tipo de retorno de base::get_this
es base&
. si quieres obtener un objeto derived
deberás llamar a get_this
través de un puntero derived
(o downcast tu base&
a un derived&
). Tenga en cuenta que así es como funciona la covarianza de tipo de retorno en Java, C ++, D ...
base* pbase = new base();
base* pderived = new derived();
derived* pderived2 = new derived();
base& a = pbase->get_this(); // call implementation in base, return base&
base& b = pderived->get_this(); // call implementation in derived, return base&
derived& c = pderived2->get_this(); // call implementation in derived, return derived&
El tipo estático de pd
es base *
. Por lo tanto, cuando el compilador busca la función miembro get_this()
, solo encuentra base::get_this()
. El tipo de retorno de base::get_this()
es base&
, que no es convertible a derived&
. De ahí el error.
Me gustaría agregar a la respuesta de Novelocrat remitiéndote a la sección 10.3, párrafo 8 del borrador de C ++ estándar ( haz clic aquí) que explica en qué caso el tipo estático del puntero devuelto es Derivado * en oposición a Base *. Básicamente, si hubiera llamado get_this()
través de un puntero a la clase dervied, habría obtenido el tipo correcto sin error del compilador.
Aquí hay una cita del estándar junto con un ejemplo (también del estándar):
Si el tipo de devolución de D :: f difiere del tipo de devolución de B :: f, el tipo de clase en el tipo de devolución de D :: f se completará en el punto de declaración de D :: f o será la clase tipo D. Cuando se llama a la función de anulación como el desbordamiento final de la función anulada, su resultado se convierte al tipo devuelto por la función anulada (elegida estáticamente) (5.2.2). [Ejemplo:
class B { };
class D : private B { friend class Derived; };
struct Base {
virtual void vf1();
virtual void vf2();
virtual void vf3();
virtual B* vf4();
virtual B* vf5();
void f();
};
struct No_good : public Base {
D* vf4(); // error: B (base class of D) inaccessible
};
class A;
struct Derived : public Base {
void vf1(); // virtual and overrides Base::vf1()
void vf2(int); // not virtual, hides Base::vf2()
char vf3(); // error: invalid difference in return type only
D* vf4(); // OK: returns pointer to derived class
A* vf5(); // error: returns pointer to incomplete class
void f();
};
void g() {
Derived d;
Base* bp = &d; // standard conversion:
// Derived* to Base*
bp->vf1(); // calls Derived::vf1()
bp->vf2(); // calls Base::vf2()
bp->f(); // calls Base::f() (not virtual)
B* p = bp->vf4(); // calls Derived::pf() and converts the
// result to B*
Derived* dp = &d;
D* q = dp->vf4(); // calls Derived::pf() and does not
// convert the result to B*
dp->vf2(); // ill-formed: argument mismatch
}
La compatibilidad con los tipos de retorno covariantes de C ++ solo funcionará, siempre que conozca el tipo derivado. Para downcast una clase base a una posible clase derivada, simplemente use dynamic_cast<derived>(base_ref)
para determinar si base_ref coincide con el tipo derivado real:
int main () {
base* pd = new derived();
derived& x = dynamic_cast<derived&>(*pd); // Will throw an exception if pd
// isn''t a ''derived''
x.fn();
return 0;
}
O alternativamente:
int main () {
base* pd = new derived();
derived* x = dynamic_cast<derived*>(pd); // Will return nullptr if pd isn''t
// a ''derived''
if(x) {
x->fn();
}
else {
// dynamic_cast<derived*> failed ...
}
return 0;
}
c ++ admite tipos de retorno covariantes para las clases derivadas, pero como las otras respuestas describen, no se puede obtener llamando a la clase base ( pd->get_this()
) aquí.
También podría considerar el polimorfismo estático para verificar el cumplimiento del tipo en tiempo de compilación, si no puede usar RTTI , manejo de excepciones o si desea un enlace de tipo ajustado (sin sobrecarga de vtable).