simple resueltos multiple hijo herencia ejercicios ejemplos derivada codigo clases clase c++ class forward-declaration

c++ - resueltos - Declaración anticipada de una clase base



herencia c++ (6)

Intento crear archivos de encabezado adecuados que no incluyan muchos otros archivos para mantenerlos limpios y para acelerar el tiempo de compilación.

Me encontré con dos problemas al hacer esto:

  1. La declaración directa en clases base no funciona.

    class B; class A : public B { // ... }

  2. La declaración directa en las clases de ETS no funciona.

    namespace std { class string; } class A { string aStringToTest; }

¿Cómo resuelvo estos problemas?


En ambos casos, el compilador necesita saber el tamaño del tipo. Por lo tanto, una declaración directa no será suficiente. Una clase base podría agregar miembros o requerir una tabla virtual. El miembro de cadena requerirá que el tamaño de la clase aumente para almacenar el tamaño de la clase de cadena STL.

Las clases de STL a menudo no son recomendables ya que las implementaciones comúnmente incluyen instancias explícitas de plantillas que aceleran la compilación.


Intentas demasiado para resolver algo que no es realmente un problema. Utilice los archivos de encabezado que necesita y reduzca, DONDE SEA POSIBLE, el requisito para ellos. Pero no intentes llevarlo al extremo porque vas a fallar.

En algunos casos, la expresión PIMPL puede ayudarlo, pero no aquí.


Para sus clases base, necesita tener la definición de tipo completo, no solo una declaración. Los encabezados de tipo derivado necesitarán #incluir el encabezado para sus clases base.

Para las clases en el espacio de nombres estándar, debe incluir el encabezado apropiado - <cadena> en este caso - y luego hacer una de estas tres cosas:

  1. Califique completamente el tipo: std :: string aStringToTest

  2. Ponga una declaración using para ese tipo: using std :: string;

  3. Ponga una declaración de uso para el espacio de nombres estándar: using namespace std;


> Parece que la declaración directa es inútil para clases base y stl.

Corrección ... La declaración directa es INAPROPIADA para las clases base y el miembro del objeto. (No es "inútil", es "inaplicable").

Una clase base DEBE ser declarada (no declarada) al ser declarada como una clase base de otra clase.

Un miembro de objeto DEBE ser declarado (no declarado) cuando sea declarado por otra clase, o como parámetro, o como un valor de retorno. NOTA: by-reference o by-pointer no tiene esa restricción.

Corrección ... La declaración directa de las clases STL es - según ISO 14882 - comportamiento indefinido. http://www.gotw.ca/gotw/034.htm


Como contestó Earwicker anteriormente, no puede usar declaraciones directas en ninguno de esos casos, ya que el compilador necesita saber el tamaño de la clase.

Solo puede usar una declaración directa en un conjunto de operaciones:

  • declarar funciones que toman la clase declarada como parámetros o la devuelve
  • declarar punteros de miembro o referencias a la clase declarada hacia adelante
  • declarar variables estáticas del tipo declarado hacia delante en la definición de clase

No puedes usarlo para

  • declarar un atributo de miembro del tipo dado (el compilador requiere tamaño)
  • definir o crear un objeto del tipo o eliminarlo
  • llamar a cualquier método estático o miembro de la clase o acceder a cualquier miembro o atributo estático

(¿Olvidé alguno?)

Tenga en cuenta que declarar un auto_ptr no es lo mismo que declarar un puntero sin procesar, ya que la creación de instancias auto_ptr intentará eliminar el puntero cuando se salga del alcance y eliminar requiere la declaración completa del tipo. Si usa un auto_ptr para mantener un tipo declarado hacia adelante, tendrá que proporcionar un destructor (incluso si está vacío) y definirlo después de que se haya visto la declaración de clase completa.

También hay algunas otras sutilezas. Cuando reenvía declara una clase, le está diciendo al compilador que será una clase. Esto significa que no puede ser un enum o un typedef en otro tipo. Ese es el problema que está recibiendo cuando intenta reenviar declare std::string , ya que es un typedef de una instanciación específica de una plantilla:

typedef basic_string<char> string; // aproximate

Para reenviar cadena de declaración, necesitarás reenviar para declarar la plantilla basic_string y luego crear typedef . El problema es que el estándar no establece el número de parámetros que basic_string plantilla basic_string , solo establece que si toma más de un parámetro, el resto de los parámetros debe tener un tipo predeterminado para que basic_string la expresión anterior. Esto significa que no hay una forma estándar de reenviar la plantilla.

Si, por otro lado, desea reenviar declarar una plantilla no estándar (no STL), puede hacerlo mientras sepa la cantidad de parámetros:

template <typename T, typename U> class Test; // correct //template <typename T> class Test; // incorrect even if U has a default type template <typename T, typename U = int> class Test { // ... };

Al final, el consejo que le dio Roddy: adelante declare todo lo que pueda, pero suponga que algunas cosas deben ser incluidas.


El primer problema que no puedes resolver

El segundo problema no tiene nada que ver con las clases de biblioteca estándar. Es porque declaras una instancia de la clase como miembro de tu propia clase.

Ambos problemas se deben al requisito de que el compilador debe poder determinar el tamaño total de una clase a partir de su definición.

Sin embargo, el compilador puede calcular el tamaño de un puntero a una clase, incluso si todavía no tiene la definición completa. Entonces, una posible solución en tales casos es tener un puntero (o referencia) miembro en la clase consumidora.

No es de mucha ayuda en el caso de la clase base, porque no obtendrá una relación ''es una''.

Tampoco vale la pena hacer algo como std::string . En primer lugar, se supone que es una envoltura conveniente alrededor de un búfer de caracteres, para evitar que haga la gestión de la memoria en algo tan simple. Si mantiene un puntero sobre él, solo para evitar incluir el encabezado, probablemente esté tomando una buena idea demasiado lejos.

En segundo lugar (como se señala en un comentario), std::string es un typedef a std::basic_string<char> . Por lo tanto, debe reenviar declarar (y luego usar) eso, en cambio, para entonces las cosas se vuelven muy oscuras y difíciles de leer, que es otro tipo de costo. ¿Realmente vale la pena?