c++ - polimorfismo - herencia programacion
En C++, ¿es posible reenviar declarar una clase como heredera de otra clase? (4)
Las declaraciones a futuro son declaraciones, no definiciones. Por lo tanto, cualquier cosa que requiera la declaración de una clase (como punteros a esa clase) solo necesita la declaración directa. Sin embargo, cualquier cosa que requiera la definición, es decir, necesitaría conocer la estructura real de la clase, no funcionaría solo con la declaración directa.
Las clases derivadas definitivamente necesitan conocer la estructura de su padre, no solo que el padre existe, por lo que una declaración directa sería insuficiente.
Sé que puedo hacer:
class Foo;
pero puedo reenviar declarar una clase heredando de otra, como:
class Bar {};
class Foo: public Bar;
Un ejemplo de caso de uso serían los tipos de retorno de referencia de covariante.
// somewhere.h
class RA {}
class RB : public RA {}
... y luego en otro encabezado que no incluye somewhere.h
// other.h
class RA;
class A {
public:
virtual RA* Foo(); // this only needs the forward deceleration
}
class RB : public RA; // invalid but...
class B {
public:
virtual RB* Foo(); //
}
La única información que el compilador debería necesitar para procesar la declaración de RB* B:Foo()
es que RB
tiene RA
como una clase base pública. Ahora claramente necesitarás algo en algún lugar. Si pretendes desreferenciar los valores de retorno de Foo
. Sin embargo, si algunos clientes nunca llaman a Foo
, entonces no hay ninguna razón para que ellos incluyan alguna parte.h, lo que podría acelerar significativamente la compilación.
No creo que sea útil. Considera: has definido una clase, Bar:
class Bar {
public:
void frob();
};
Ahora declaras una clase Foo:
class Foo;
Todo lo que puedes hacer con Foo es construir un puntero hacia él. Ahora, supongamos que agrega la información que Foo
deriva de Bar
:
class Foo: public Bar;
¿Qué puedes hacer ahora que no pudiste hacer antes? Creo que todo lo que puedes hacer es aceptar un puntero a Foo
y convertirlo en un puntero a Bar
, luego usar ese puntero.
void frob(Foo* f) {
Bar *b = (Bar)f;
b->frob();
}
Sin embargo, debe haber generado el puntero en otro lugar, por lo que podría haber aceptado un puntero a Bar
lugar.
void frob(Bar* b) {
b->frob();
}
No, no es posible reenviar declarar la herencia, incluso si solo se trata de punteros. Cuando se trata de conversiones entre punteros, a veces el compilador tiene que conocer los detalles de la clase para hacer la conversión correctamente. Este es el caso de la herencia múltiple. (Podría tratarse de casos especiales de partes de la jerarquía que solo usan herencia individual, pero que no forman parte del lenguaje).
Considere el siguiente caso trivial:
#include <stdio.h>
class A { int x; };
class B { int y; };
class C: public A, public B { int z; };
void main()
{
C c; A *pa = &c; B *pb = &c; C *pc = &c;
printf("A: %p, B: %p, C: %p/n", pa, pb, pc);
}
La salida que recibí (usando Visual Studio de 32 bits 2010) es:
A: 0018F748, B: 0018F74C, C: 0018F748
Por lo tanto, para la herencia múltiple, cuando se convierte entre punteros relacionados, el compilador tiene que insertar una aritmética de puntero para obtener las conversiones correctas.
Esta es la razón por la cual, incluso si se trata solo de punteros, no se puede reenviar para declarar la herencia.
En cuanto a por qué sería útil, mejoraría los tiempos de compilación cuando desee utilizar tipos de devolución de covariantes en lugar de utilizar conversiones. Por ejemplo, esto no compilará:
class RA;
class A { public: virtual RA *fooRet(); };
class RB;
class B : public A { public: virtual RB *fooRet(); };
Pero esto hará:
class RA;
class A { public: virtual RA *fooRet(); };
class RA { int x; };
class RB : public RA{ int y; };
class B : public A { public: virtual RB *fooRet(); };
Esto es útil cuando tiene objetos de tipo B (no punteros ni referencias). En este caso, el compilador es lo suficientemente inteligente como para utilizar una llamada de función directa, y puede utilizar el tipo de devolución de RB * directamente sin conversión. En este caso, generalmente sigo adelante y hago el retorno tipo RA * y hago un lanzamiento estático en el valor de retorno.
Una declaración directa solo es realmente útil para decirle al compilador que existe una clase con ese nombre y será declarada y definida en otro lugar. No se puede usar en ningún caso donde el compilador necesite información contextual sobre la clase, ni sirve para nada al compilador para contarle solo un poco sobre la clase. (En general, solo puede usar la declaración directa cuando se refiere a esa clase sin otro contexto, por ejemplo, como parámetro o valor de retorno).
Por lo tanto, no puedes reenviar declarar Bar en ningún escenario donde luego lo utilices para ayudar a declarar Foo, y no tiene sentido tener una declaración directa que incluya la clase base. ¿Qué te dice eso además? ¿nada?