2010-09-03 15 views
14

Considere los dos escenarios siguientes (editado sólo para completar toda la cuestión y hacerlo más claro)Declaración vs progreso se cuentan las

Caso 1: (duerma compile como acertadamente menciona a continuación)

//B.h 
#ifndef B_H 
#define B_H 
#include "B.h" 

class A; 

class B { 
     A obj; 
     public: 
     void printA_thruB(); 

     }; 
#endif 

//B.cpp 
#include "B.h" 
#include <iostream> 

void B::printA_thruB(){ 
     obj.printA(); 
     } 


//A.h; 
#ifndef A_H 
#define A_H 

#include "A.h" 

class A { 
     int a; 
     public: 
     A(); 
     void printA(); 

     }; 
#endif 

//A.cpp       
#include "A.h"      
#include <iostream>    

A::A(){       
     a=10;      
     }       

void A::printA()     
{         
std::cout<<"A:"<<a<<std::endl;  
} 


//main.cpp 
#include "B.h" 
    #include<iostream> 
using namespace std; 

int main() 
{ 
B obj; 
obj.printA_thruB(); 
} 

Caso 2: (las únicas modificaciones ... funciona sin error compiliation)

//B.h 

#include "A.h" //Add this line 
//class A;  //comment out this line 

Supongamos tanto el a.cpp y B.cpp se cumplen juntos. ¿Los dos escenarios anteriores hacen alguna diferencia? ¿Hay alguna razón para preferir un método sobre el otro?

Edit: Entonces, ¿cómo puedo hacer que el escenario 1 funcione.

+1

Has compilado A.cpp y B.cpp juntos, pero ¿qué has hecho con A.h y B.h? –

+0

@Dennis Hizo la pregunta más clara para usted – Sii

Respuesta

13

El caso 1 producirá un error de "tipo incompleto" al compilar B.cpp. Debido a que la clase B contiene un objeto de clase A, la definición (y en particular el tamaño) de la clase A debe completarse antes de la definición de clase B.

Como alternativa, puede optar por hacer que una_variable sea un puntero o referencia a clase A, y en ese caso su declaración directa sería suficiente en Bh Aún necesitaría una definición completa de A en B.cpp (suponiendo que haya hecho un uso real de las funciones/datos de los miembros A).

+2

Una aclaración. El Caso 1 solo producirá un error de compilación si intenta llamar a cualquier método en A. En este caso, debería dar un error ya que llamará implícitamente al constructor. Pero en un escenario diferente si uno de los métodos de B simplemente recibía un puntero a un objeto A, que luego pasaba a otro método/función que estaba fuera de la responsabilidad de B, entonces el compilador estaría bien sin conocer la definición de A. – Hitesh

+0

tienes razón, Drew, obtengo un error de compilación. Entonces, ¿cómo puedo superarlo? Pensé que las declaraciones futuras se pueden utilizar en lugar de la inclusión del encabezado. – Sii

+0

@Hitesh: No. El Caso 1 siempre producirá un error de compilación porque el compilador no conoce el tamaño de (A) y, por lo tanto, no puede saber cómo diseñar un objeto B. –

4

Necesita utilizar declaraciones forward en los casos en que tenga clases que se refieran entre sí.

//A.h 

class B; 

class A { 
    B* someVar; 
} 

//B.h 
#include <A.h> 

class B { 
    A* someVar; 
} 

Pero no hay ningún beneficio para hacerlo en el caso que usted presentó.

+0

Gracias por la información. que pasa si B tiene acceso a alguna variable de A en mi caso. – Sii

+0

En su caso, realmente desea utilizar la inclusión explícita de A.h dentro de B.h.Solo he visto dos ejemplos de referencias de avance: el primero que mencioné anteriormente con respecto a las referencias circulares y el segundo fue un caso en el que un proveedor proporcionó archivos de encabezado que tenían algunas clases definidas solo como referencias directas. Habían fregado sus archivos de encabezado para proteger su información de propiedad. El resultado neto es que fue una pesadilla trabajar con su API, no lo recomiendo. – Hitesh

+0

Estaba tratando de hacer algo similar ... eliminar el encabezado de la interfaz (.h) y reenviar declarar la clase que estoy tratando de usar. – Sii

2

Si quisiste representar some_variable como un puntero, la práctica frecuentemente recomendada es usar declaraciones forward siempre que sea posible para evitar la sobrecarga de los includes y tiempos de compilación más largos.

Estoy a favor de las mejores prácticas, pero realmente me gusta usar IDEs que tengan buenas funciones de navegación de código y los reenvíos causen un problema allí, al menos con Netbeans. Cada vez que trato de navegar a una declaración de tipo, siempre termino en el forward y no en el archivo .h que contiene la declaración real. Estoy dispuesto a aceptar más tiempo de compilación para facilitar la navegación. Tal vez esto es solo un problema con Netbeans :)

.. oh sí ... Si nos fijamos en las preguntas relacionadas a la derecha de su pregunta, encontrará mucha información adicional de declaraciones adelante.

+0

Esto es lo que estaba tratando de evitar ... la inclusión de encabezado no deseado. – Sii

3

Piense como un compilador. Para crear un A dentro de B, el compilador debe saber cómo crear un A, y la única forma de hacerlo es tener la definición completa. La declaración directa le dice al compilador que existe la clase A sin describir su aspecto; esto es adecuado para definir un puntero o una referencia. Cuando llegue el momento de usar ese puntero o referencia, se requerirá la definición de clase completa.

+0

Entonces, si incluyo el encabezado '" A.h "' en el archivo B.cpp y solo reenvío la clase A en B.h ¿no se supone que funciona? – Sii

+0

@MrProg, no, no funcionará - Bh define cómo crear una B, pero no puede crear una B sin saber cómo crear una A porque B contiene una A. Puede almacenar un puntero o referencia a una A sin saber cómo crear una A, por lo que la referencia directa puede ser útil, pero no en su caso. –

+0

si cambio el 'A obj' por' A * obj' y hago lo que mencioné anteriormente, también hago que el 'int a' en la clase A sea público, puedo compilarlo ... pero obtengo el error de segmentación. Así que supongo que mi comprensión de la declaración directa debe ser repensada. – Sii

8

La declaración de reenvío no sustituye la inclusión del archivo de encabezado.

Como el propio nombre indica, declaración adelantada es sólo un Declarationy no una definición.

Así que declararás diciendo el compilador que es una clase y yo solo lo declaro aquí y te daré la definición de cuándo voy a usarlo. Entonces, normalmente usted forward declare en el archivo de encabezado y #include en el archivo .cpp donde usará use los miembros de la clase forward declarada.

De esta manera, lo que hacen es decir, allí donde va a incluir el archivo de cabecera no será sólo una declaración de la clase en lugar de todo el contenido #included ...

Pero una vez dicho esto, cuando el compilador requiere la definición de la clase, debe ser #included ..

Así, en su caso A obj; requiere la definición de class A y por lo tanto se debe #include ..

me hice una pregunta similar here y otro similar question que tiene también una respuesta agradable ...

creo que sirve ..

+0

Al incluir los encabezados hacemos lo mismo ... es solo declaración ... la definición va en el archivo de fuentes .cpp. Así que si cambio mi 'A obj' a' A * obj' que es un puntero, se supone que funciona correctamente. (Pero no) – Sii

+0

Gracias por la ayuda ... Me he confundido lo suficiente por un día que pienso. – Sii

0

Para el caso 1, compilador se quejará con el "tipo incompleto" para la clase B porque la clase B contiene una clase de un objeto, y no se lo dijiste B ningún detalle de la clase a, por lo que el compilador cann't decidir el tamaño del objeto B.

Para su caso, se puede utilizar A& obj o A* obj en lugar de A obj, ya que el tamaño de una referencia/puntero es const (4/8 para CPU de 32bit/64bit).

Cuestiones relacionadas