2011-09-13 22 views
6

Así que después de buscar mucho para encontrar una respuesta a mi pregunta, finalmente abandoné mis habilidades con Google.Redefinir un typedef en la clase derivada?

I tienen una clase base Base, y una clase derivada Derivado. Quiero sobrescribir un tipo en la clase Base con uno en la clase Derived. He aquí un ejemplo:

class Apple { 
public: 
    Apple() { } 

    // ... 

}; 

class Orange { 
public: 
    Orange() { } 

    // ... 

}; 

class Base { 
public: 
    typedef Apple fruit; 

    // ... 

    virtual fruit func() { return Apple(); } 
}; 

class Derived : public Base { 
public: 
    typedef Orange fruit; 

    // ... 

    fruit func() override { return Orange(); } // <-- Error C2555! 
}; 

Este código no funciona, y le da una opción más arriba

C2555 error ('Derived::func': overriding virtual function return type differs and is not covariant from 'Base::func'). 

fue una de las soluciones que he probado. También intenté crear una clase anidada virtual en el Base, y redefiní en Derivada, y eso no compiló también (también fue muy desordenado).

Asimismo, no puede derivar manzanas y naranjas de la misma clase base para volver puntero/referencia a su clase padre en Base y Derivado. Necesito devolver físicamente una instancia del objeto.

  1. ¿Hay alguna forma en que pueda declarar typedefs abstractos?
  2. Si no es así, ¿hay alguna otra solución que pueda lograr lo que estoy tratando de hacer?

Respuesta

6

En primer lugar, un vistazo a la siguiente sintaxis:

fruit func() override { return Orange(); } 

¿Cuál es override? En C++ 03, no hay tal palabra clave. Solo en C++ 11. Así que asegúrese de estar utilizando un compilador que conozca esta palabra clave.

En segundo lugar, en la clase derivada, fruit es de hecho Orange. Redefinir el typedef no es un problema. El problema es que Orange y Apple no son del tipo covariante. Derivar uno de otro los hará covariantes. En su caso, debe derivar Orange de Apple para que funcione.

Nota usted tiene que cambio el tipo de retorno de fruit a cualquiera fruit* o fruit&.

class Orange : public Apple {}; //correct - your code will work 

class Apple : public Orange {}; //incorrect - your code will not work 

La idea es, en la clase base, el tipo de retorno debe ser puntero/referencia de tipo base (que es Apple), y en la clase derivada, el tipo de retorno puede ser puntero/referencia del tipo Apple o cualquier clase que se derive de él.

Por cierto, ¿tiene sentido esto? Derivando Orange de Apple?

¿Qué le parece el siguiente diseño de clase?

class Fruit {}; 
class Apple : public Fruit {}; 
class Orange : public Fruit {}; 

class Base 
{ 
    virtual Fruit* f(); 
}; 

class Derived : public Base 
{ 
    virtual Fruit* f(); 
}; 

No necesita usar typedef.

+0

La palabra clave 'override' ayuda a eliminar los errores humanos (como los errores tipográficos) al hacer que el compilador compruebe si la función en realidad está anulando cualquier método base o no. En cuanto al problema, soy consciente de eso. ¿Hay alguna forma en que podría hacerlos covariantes sin el uso de punteros o referencias? – Zeenobit

+0

Así que supongo que no hay solución para que esto funcione. Gracias gente. Trataré de usar una jerarquía diferente. – Zeenobit

0

Realmente no puede hacer eso, si tiene un objeto de valor, debe saber de qué tipo es. (Esta es una de las principales diferencias entre un lenguaje estáticamente tipado como C++ y los lenguajes de tipado dinámico como Ruby y Python.)

Hay muchas formas de evitar esto. En primer lugar, supongamos que Apple y Oragne tienen una clase base común llamada Fruit. Una solución es asignar dinámicamente el objeto usando new y devolver un Fruit -pointer.

Otra solución, en lugar de devolver un valor, su función podría tener un puntero o referencia a una fruta que luego podría rellenar.

Sin embargo, otra solución es tener algún tipo de objeto contenedor que internamente obstaculicen ya sea una manzana o naranja. De esa forma podrías devolverlo.

1

Esto no tiene mucho sentido para empezar. Un Derived debe ser capaz de ser utilizado en cualquier lugar se espera una Base, por lo que debería ser capaz de hacer

Base *foo = new Base(); 
Apple x = foo->func(); // this is fine 

Base *bar = new Derived(); 
Apple y = foo->func(); // oops... 

Creo que hay que buscar en un diseño diferente. No está claro cuál es su objetivo aquí, pero supongo que puede querer Base como una plantilla de clase con Fruit como parámetro de plantilla, o tal vez deba deshacerse de la herencia por completo.

0

El error informado le dice todo lo que necesita. Lo que significa es que el tipo devuelto en el método Derived debe derivarse del tipo devuelto en el método base.

Esto es para que si el método base se llama virtualmente, el objeto devuelto se puede ver como el que devuelve el método base.

En realidad, debe devolver un puntero o una referencia para hacerlo.

Lo que se necesita hacer es definir una nueva clase Fruit base, y tienen Apple y Orange derivar de ella.

Luego tenga func() devuelva un Fruit*.

Eso le dejará con el problema de asegurarse de que Fruit* es delete 'd en algún momento.

Con un poco más de contexto, sospecho que las plantillas (probablemente delgadas) son la solución que buscas, en lugar de la herencia.

Cuestiones relacionadas