Pregunta

Tengo un problema que creo que está relacionado con las declaraciones directas, pero tal vez no.

Aquí está el código relevante:

A.h

#ifndef A_H_
#define A_H_

#include "B.h"

class A
{
    private:
        B b;

    public:
        A() : b(*this) {}

        void bar() {}
};

#endif /*A_H_*/

B.h

#ifndef B_H_
#define B_H_

#include "A.h"

class A;

class B
{
    private:
        A& a;

    public:
        B(A& a) : a(a) {}

        void foo() { /*a.bar();*/ } //doesn't compile
};

#endif /*B_H_*/

main.cpp

#include "A.h"

int main()
{
    A a;

    return 0;
}

El problema parece ser con la invocación de A :: bar (). El programa se compila correctamente hasta que intento llamar a este método, momento en el que obtengo dos errores:

  

error: uso no válido de tipo incompleto & # 8216; struct A & # 8217;

     

error: declaración hacia adelante de & # 8216; struct A & # 8217;

Supongo que esto se debe a que A :: bar () aún no se ha definido o declarado ya que ambos encabezados se refieren entre sí. Sin embargo, reenvié la clase A y no sé qué más debo hacer. Soy nuevo en C ++, así que por favor perdóname. No pude encontrar la respuesta a esta pregunta en ningún otro lugar en línea. Como siempre, gracias de antemano!

¿Fue útil?

Solución

Tienes una referencia circular, por lo que debes separar B.h. Pruebe algo como:

B.h:

#ifndef B_H_
#define B_H_

// don't include A.h here!

class A;

class B
{
   private:
      A& a;

   public:
      B(A& a) : a(a) {}

      void foo();
};

#endif /*B_H_*/

B.cpp:

#include "B.h"
#include "A.h"

void B::foo() { a.bar(); } // now you're ok

Editar: explicación de por qué necesita dividirlo en dos archivos:

La clase B contiene una referencia a A, que puede ser un tipo llamado incompleto . No puede llamar a ninguna función porque el compilador aún no sabe qué diablos es <=>, solo sabe que es una clase de algún tipo. Una vez que incluya A.h (en el archivo .cpp), entonces <=> es un tipo completo y puede hacer lo que quiera con él.

No puede mantener todo en un archivo de encabezado porque obtendrá una referencia circular. Estás evitando un bucle infinito con tus guardias de inclusión, pero estás obteniendo algo que no quieres. Mire con qué termina el compilador cuando compila main.cpp, como lo tenía antes:

// #include "A.h" ==>
#define A_H_

// #include "B.h" ==>
#define B_H_

// #include "A.h" ==> nothing happens! (since A_H_ is already defined)

class A;

class B {
private:
    A& a;

public:
    B(A& a) : a(a) {}

    void foo() { a.bar(); } // <-- what the heck is A here?
                            //     it's not defined until below
};

class A {
private:
   B b;

public:
   A() : b(*this) {}

   void bar() {}
};

int main() {
    A a;
    return 0;
}

Otros consejos

La línea #include<file.h> simplemente reemplazará la línea con el contenido de file.h. Entonces, cuando la computadora intenta compilar su main.cpp, juntará todo, lo que se parece a lo siguiente. En el lugar donde desea usar A :: bar (), no se ha definido.

// result from #include "A.h"

#ifndef A_H_
#define A_H_

// Now, #include "B.h" in A.h will get you the following
#ifndef B_H_
#define B_H_

// here you include "A.h" again, but now it has no effect
// since A_H_ is already defined

class A;

class B
{
    private:
            A& a;
    public:
            B(A& a) : a(a) {}
            // Oops, you want to use a.bar() but it is not defined yet
            void foo() { /*a.bar();*/ } 
};

#endif /*B_H_*/

class A
{
    private:
            B b;
    public:
            A() : b(*this) {}
            void bar() {}
};
#endif /*A_H_*/

// now this is your main function
int main()
{
    A a;
    return 0;
}

Como varios otros han mencionado, la referencia circular parece ser su problema. Otra frase para esto sería & Quot; dependencia mutua & Quot ;. Sin embargo, en lugar de intentar encontrar la sintaxis adecuada para que su aplicación se compile y se ejecute (supongo que el problema real existe en un programa un poco más avanzado que lo que ha publicado), le animo a que ataque el problema desde un objeto. punto de vista de diseño orientado.

Como regla general, las dependencias mutuas deben evitarse siempre que sea posible. Me he encontrado con este problema en mi propio código antes (lo que resultó en varios días de depuración, arrancándome el pelo y maldiciendo mi compilador), y así es como finalmente pude superarlo. Presentaré una versión diluida de mi propio problema como un ejemplo concreto de cómo abordar el problema, por lo que espero que pueda extraer el significado oculto detrás de todo, y todo tendrá sentido al final.

Digamos que tenemos dos clases: Data y DataAnalyzer

Los datos contienen una referencia a DataAnalyzer (usado para analizar los datos), y DataAnalyzer contiene una referencia a Data (los datos a analizar) - ¡dependencia mutua! Para eliminar esta dependencia, extraemos una interfaz (en C ++, una clase virtual pura) de DataAnalyzer que define los métodos / atributos públicos requeridos en DataAnalyzer. Puede verse algo así como:

class IAnalyzer
{
public:
    virtual void Analyze () = 0;
};

Cuando definimos DataAnalyzer, lo hacemos de la siguiente manera:

class DataAnalyzer : public IAnalyzer
{
public:
    DataAnalyzer (Data* data);

    virtual void Analyze (); // defined in DataAnalyzer.cpp
};

Y los datos se ven así:

class Data
{
public:
    Data ();

    IAnalyzer* Analyzer;
};

En algún lugar, en su clase de controlador, puede tener algo como:

void main ()
{
    Data*  data = new Data ();
    data->Analyzer = new DataAnalyzer (data);
}

Ahora, los datos se mantienen por sí mismos (hasta donde se sabe, IAnalyzer no requiere una referencia a los datos), y solo DataAnalyzer depende de los datos. Si desea continuar, puede continuar eliminando la dependencia de DataAnalyzer en los datos, pero por el simple hecho de romper la dependencia mutua, esto debería ser suficiente.

Advertencia: no he compilado probado este código, por lo que puede requerir algunos ajustes menores para compilar y ejecutar correctamente.

¡Buena suerte!

Si realmente quieres que B :: foo esté en línea, puedes hacer la implementación en B.h, aunque no lo recomendaría.

B.h:

#ifndef B_H_
#define B_H_

// Forward declaration of A.
class A;

class B
{
private:
    A& a;

public:
    B(A& a) : a(a) {}

    void foo();
};

// Include definition of A after definition of B.
#include "A.h"

inline void B::foo()
{
    a.bar();
}

#endif /*B_H_*/

A.h:

// Include definition of B before definition of A.
// This must be done before the ifndef A_H_ include sentry,
// otherwise A.h cannot be included without including A.h first.
#include "B.h"

#ifndef A_H_
#define A_H_

class A
{
private:
    B b;

public:
    A() : b(*this) {}

    void bar() {}
};

#endif /*A_H_*/

En B.h, usted incluye A.h, así como la declaración progresiva A.

Debe separar B.h en B.h y B.cpp, o eliminar la declaración de reenvío.

PD También tienes una dependencia circular. A.h incluye a B.h, y viceversa. Sin embargo, tus guardias están captando el problema;)

Para agregar a la otra respuesta (referencia circular, que es la respuesta correcta), si viene de C # / Java, comprenda que C ++ es diferente, ya que los archivos se analizan en orden (en lugar de considerarse como un todo ) Por lo tanto, debe tener cuidado para asegurarse de que todo esté definido antes de su uso, en el orden real en que se incluyen los archivos (y / o la funcionalidad separada en archivos .cpp según corresponda).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top