Domanda

Ho un problema che penso sia correlato alle dichiarazioni a termine, ma forse no.

Ecco il codice pertinente:

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;
}

Il problema sembra essere con l'invocazione di A :: bar (). Il programma viene compilato correttamente fino a quando non tento di chiamare questo metodo a quel punto ottengo due errori:

  

errore: uso non valido del tipo incompleto & # 8216; struct A & # 8217;

     

errore: dichiarazione in avanti di & # 8216; struct A & # 8217;

Presumo che ciò sia dovuto al fatto che A :: bar () deve ancora essere definito o dichiarato poiché entrambe le intestazioni si riferiscono reciprocamente. Tuttavia, ho inoltrato la classe A dichiarata e sono in perdita su cos'altro devo fare. Sono nuovo di C ++, quindi per favore perdonami. Non sono riuscito a trovare la risposta a questa domanda altrove online. Come sempre, grazie in anticipo!

È stato utile?

Soluzione

Hai un riferimento circolare, quindi devi separare B.h. Prova qualcosa del tipo:

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

Modifica: spiegazione del motivo per cui è necessario dividerlo in due file:

La classe B contiene un riferimento a A, che può essere un cosiddetto tipo incompleto . Non puoi richiamare alcuna funzione perché il compilatore non sa ancora cosa diavolo <=> - sa solo che è una classe di qualche tipo. Dopo aver incluso A.h (nel file .cpp), <=> è un tipo completo e puoi fare quello che ti piace.

Non puoi tenere il tutto in un file di intestazione perché otterrai un riferimento circolare. Stai impedendo un loop infinito con le tue guardie di inclusione, ma stai ottenendo qualcosa che non vuoi. Guarda cosa finisce il compilatore quando compili main.cpp, come avevi prima:

// #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;
}

Altri suggerimenti

La riga #include<file.h> sostituirà semplicemente la riga con il contenuto di file.h. Quindi, quando il computer tenta di compilare il main.cpp, riunirà tutto, il che è simile al seguente. Nel punto in cui si desidera utilizzare A :: bar (), non è stato definito.

// 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;
}

Come molti altri hanno già detto, il riferimento circolare sembra essere il tuo problema. Un'altra frase per questo sarebbe & Quot; dipendenza reciproca & Quot ;. Tuttavia, anziché tentare di trovare la sintassi corretta per compilare ed eseguire l'applicazione (suppongo che il problema reale esista in un programma leggermente più avanzato di quello che hai pubblicato), ti incoraggio ad attaccare il problema da un oggetto- punto di vista del design orientato.

Come regola generale, le dipendenze reciproche dovrebbero essere evitate laddove possibile. Ho riscontrato questo problema nel mio codice prima (che ha provocato diversi giorni di debug, strappandomi i capelli e maledendo il mio compilatore), ed ecco come sono finalmente riuscito a risolverlo. Presenterò una versione annacquata del mio problema come un esempio concreto di come affrontare il problema, quindi spero che tu possa estrarre il significato nascosto dietro tutto, e alla fine avrà un senso.

Diciamo che abbiamo due classi: Data e DataAnalyzer

I dati contengono un riferimento a DataAnalyzer (utilizzato per analizzare i dati) e DataAnalyzer contiene un riferimento a Dati (i dati da analizzare): dipendenza reciproca! Per eliminare questa dipendenza, estraiamo un'interfaccia (in C ++, una pura classe virtuale) da DataAnalyzer che definisce i metodi / attributi pubblici richiesti su DataAnalyzer. Potrebbe assomigliare a:

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

Quando definiamo DataAnalyzer, lo facciamo come segue:

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

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

E i dati sembrano:

class Data
{
public:
    Data ();

    IAnalyzer* Analyzer;
};

Da qualche parte, nella tua classe di controller, potresti avere qualcosa del tipo:

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

Ora, i dati si distinguono da soli (per quanto ne sa, IAnalyzer non richiede un riferimento ai dati) e solo DataAnalyzer dipende dai dati. Se vuoi continuare, potresti continuare a rimuovere la dipendenza di DataAnalyzer dai dati, ma per motivi di semplice rottura della dipendenza reciproca, questo dovrebbe essere sufficiente.

Avviso: non ho compilato questo codice testato, quindi potrebbe essere necessario un piccolo aggiustamento per compilare ed eseguire correttamente.

Buona fortuna!

Se vuoi davvero che B :: foo sia in linea, potresti fare l'implementazione in B.h, anche se non lo consiglierei.

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_*/

In B.h, includi A.h, oltre a dichiarare in avanti A.

È necessario separare B.h in B.h e B.cpp o rimuovere la dichiarazione in avanti.

PS Hai anche una dipendenza circolare. A.h include B.h e viceversa. Le tue guardie stanno rilevando il problema;)

Per aggiungere all'altra risposta (riferimento circolare, che è la risposta corretta), se si proviene da C # / Java, capire che C ++ è diverso, in quanto i file vengono analizzati in ordine (anziché considerati nel loro insieme) ). Quindi devi stare attento a garantire che tutto sia definito prima di essere utilizzato, nell'ordine effettivo in cui i file sono inclusi (e / o funzionalità separate in file .cpp come appropriato).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top