2009-12-11 12 views
9

Il codice follwing è compilato in VC++ 6. Non capisco perché sto ricevendo l'errore di compilazione C2079: 'b' uses undefined class 'B' per il codice seguente.Inoltro dichiarazione di classe non sembra funzionare in C++

Classe B Fonte

#include "B.h" 

void B::SomeFunction() 
{ 
} 

Classe B Header

#include "A.h" 

struct A; 

class B 
{ 
    public: 
     A a; 
     void SomeFunction(); 
}; 

struct un colpo di testa

#include "B.h" 

class B; 

struct A 
{ 
    B b; 
}; 

I f Ho cambiato l'intestazione della classe B al seguente, quindi non ci saranno errori. Ma la dichiarazione di intestazione non sarà al top!

Classe Intestazione B con dichiarazione strano colpo di testa

struct A; 

class B 
{ 
    public: 
     A a; 
     void SomeFunction(); 
}; 

#include "A.h" 
+0

Puoi metterli nello stesso file di intestazione? So che non è una risposta particolarmente tecnica, ma puoi evitare tutto insieme facendo ciò se è fattibile. – ihtkwot

+0

Non importa se si mettono entrambe le definizioni di classe nello stesso file di intestazione, si deve ancora arrivare prima e non si può conoscere la dimensione dell'altra classe. – Peter

+0

grazie per i chiarimenti, sono ancora molto nuovo in questo e spero di non aver offerto un cattivo consiglio, che ero – ihtkwot

risposta

14

Per definire una classe o una struttura, il compilatore deve sapere quanto è grande ogni variabile membro della classe. Una dichiarazione anticipata non lo fa. L'ho sempre visto solo per puntatori e riferimenti (meno spesso).

Oltre a ciò, ciò che stai cercando di fare qui non può essere fatto. Non si può avere una classe A, che contiene un oggetto di un'altra classe B che contiene un oggetto di classe A. È possibile, tuttavia, hanno classe A contenere un puntatore alla classe B che contiene un oggetto di classe A.

B.cpp

#include "B.h" 

void B::SomeFunction() 
{ 
} 

B.h

#ifndef __B_h__ // idempotence - keep header from being included multiple times 
#define __B_h__ 
#include "A.h" 

class B 
{ 
public: 
    A a; 
    void SomeFunction(); 
}; 

#endif // __B_h__ 

A.h

#ifndef __A_h__ // idempotence - keep header from being included multiple times 
#define __A_h__ 
#include "B.h" 

class B; // forward declaration 

struct A 
{ 
    B *b; // use a pointer here, not an object 
}; 

#endif // __A_h__ 

due punti. Innanzitutto, assicurati di utilizzare una qualche forma di idempotence per impedire che le intestazioni vengano incluse più volte per unità di compilazione. In secondo luogo, comprendi che in C++, l'unica differenza tra classi e strutture è il livello di visibilità predefinito: le classi utilizzano la visibilità privata per impostazione predefinita mentre le strutture utilizzano la visibilità pubblica per impostazione predefinita. Le seguenti definizioni sono funzionalmente equivalenti in C++.

class MyClass 
{ 
public: // classes use private visibility by default 
    int i; 
    MyClass() : i(13) { } 
}; 

struct MyStruct 
{ 
    int i; 
    MyStruct() : i(13) { } 
}; 
+5

I caratteri di sottolineatura doppia sono riservati al compilatore. Utilizzare una convenzione di denominazione diversa. – GManNickG

+0

@GMan: Ho sempre usato questa convenzione per i miei segni di idempotenza quando il compilatore non li genera automaticamente. È specifico per un compilatore o è nelle specifiche C++? –

+1

@Matt, fa parte delle specifiche C++ che tali identificatori sono riservati. Personalmente uso "HEADER_PATH_TO_HEADER_H_INCLUDED" o "HEADER_PATH_TO_HEADER_INCLUDED" (se non ci sono ".h"). –

3
public: 
    A a; 

Si sta cercando di creare l'oggetto di A con solo in avanti dichiarazione. Il compilatore in questo momento (con solo decl avanti) non può decidere la dimensione dell'oggetto A e quindi, non può allocare la memoria richiesta per A. Quindi non è possibile creare oggetti con solo decl avanti.

Anziché sostituire con:

A* a; 

puntatore o riferimento a una definizione di classe, senza di un funzionerà benissimo.

3

Due numeri mi saltano addosso.

1: Hai scritto Struct A anziché struct A; notare la lettera "s" minuscola. Il tuo compilatore potrebbe prendere in considerazione l'equivalente, ma non penso che sia il C++ standard.

È stato definito un riferimento circolare tra A e B. Ogni oggetto A deve contenere un oggetto B, ma ogni oggetto B deve contenere un oggetto A! Questa è una contraddizione e non funzionerà mai come vorresti. Il solito modo C++ per risolvere questo problema consiste nell'utilizzare puntatori o riferimenti per A::b o B::a (o entrambi).

0

Si include anche A.h da B.h e B.h da A.h. È necessario utilizzare almeno macro di preprocessore:

#ifndef __A_H__ 
#define __A_H__ 

// A.h contents 

#endif 

in modo che il file non venga incluso più di una volta.

+0

Nel codice attuale, le macro del preprocessore sono definite ma non sono mostrate qui per semplificare il codice. – Lopper

+0

I caratteri di sottolineatura doppia sono riservati per il compilatore. Utilizzare una convenzione di denominazione diversa. – GManNickG

0

Se si crea un'istanza di A, verrà creata un'istanza di B (membro var) che creerà un'istanza di A (membro var) che creerà un'istanza di B che creerà un'istanza di A e così on ... Il compilatore non dovrebbe consentire ciò poiché richiede memoria infinita.

Per risolvere questo problema, A o B devono utilizzare un riferimento/puntatore all'altra classe.

2

dichiarazioni giusta, come

struct A; 

o

class A; 

introdurre una come tipo incompleto e rimane incompleta fino al raggiungimento fine della definizione di tipo. Ci sono cose che puoi fare con tipi incompleti e cose che non puoi. È possibile

  1. Dichiarare variabili (o membri) di tipo "puntatore a A" e "riferimento ad un"
  2. funzioni Declare che accettano argomenti di tipo A o di tipo A

è possibile tornare 't

  1. dichiarare le variabili (né i membri) di tipo a
  2. puntatori dereference a o accedere a qualsiasi membri di riferimenti a un
  3. Definire sottoclassi di A.

Nel codice si tenta di dichiarare membro struct di tipo incompleto. È illegale Sono consentiti solo puntatori e riferimenti.