2016-03-31 40 views
5

Sono nuovo al C++ ma ho una certa esperienza in Java. Durante la codifica, mi sono imbattuto in un errore che mi ha confuso. Ecco il mio codice (semplificato, ma gli errori sono gli stessi):C++: passando il puntatore a un'altra classe

Ah:

#pragma once 
#include "B.h" 
class A 
{ 
public: 
    A(); 
    void foo(); 
    void sayHello(); 
    B b; 
}; 

A.cpp:

#include "A.h" 
#include <iostream>  
A::A() {} 
void A::foo() { 
    b.bar(this); 
} 
void A::sayHello() { 
    std::cout << "Hello" << std::endl; 
} 

Bh:

#pragma once 
#include "A.h" 
class B 
{ 
public: 
    B(); 
    void bar(A *a); 
}; 

B.cpp:

#include "B.h" 
B::B(){} 
void B::bar(A *a) { 
    a->sayHello(); 
} 

voglio passare un puntatore del un oggetto alla funzione bar in B, in modo che sarò in grado di modificare e accesso a campi in bar. Stranamente, ottengo questi errori quando chiamo foo attraverso un'istanza di Un da un'altra classe:

1>------ Build started: Project: Test, Configuration: Debug Win32 ------ 
1> main.cpp 
1>d:\stuff\visual studio 2015\projects\test\test\b.h(7): error C2061: syntax error: identifier 'A' 
1> B.cpp 
1>d:\stuff\visual studio 2015\projects\test\test\a.h(9): error C3646: 'b': unknown override specifier 
1>d:\stuff\visual studio 2015\projects\test\test\a.h(9): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int 
1> A.cpp 
1>d:\stuff\visual studio 2015\projects\test\test\b.h(7): error C2061: syntax error: identifier 'A' 
1>d:\stuff\visual studio 2015\projects\test\test\a.cpp(5): error C2660: 'B::bar': function does not take 1 arguments 
1> Generating Code... 
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ========== 

Il codice funziona bene se non includo Ah in Bh e io don non passare nulla alla funzione bar.

Ho provato a google cosa potrebbe causare questi errori ma non sono stato in grado di risolvere il problema da solo perché non capisco cosa causa questi errori. Che cosa sto facendo di sbagliato?

+0

cercare "includi ciclici" e "dichiarazione anticipata" – Thomas

+0

Rifiuto circolare. Maggiori informazioni qui: http: //stackoverflow.com/questions/625799/resolve-circular-dependencies-in-c e qui: http://stackoverflow.com/questions/17865286/c-circular-include e qui: https://en.wikipedia.org/wiki/Circolare_dipendenza. TL; DR: una delle intestazioni deve essere inclusa per prima e poiché include l'altra, l'altra non può includere la prima per ottenere le definizioni necessarie perché la prima è già stata inclusa. – user4581301

risposta

2

Nel file di intestazione di Ah, si dispone:

#include "B.h" 

Nel file di intestazione di Bh, si ha:

#include "A.h" 

Si hanno una circolare includere, se la definizione di A e B dipende l'uno dall'altro.


Una soluzione semplice è quella di utilizzare forward declaration in definizione di class B:

  1. Sostituire la linea #include "A.h" nel file di B.h con class B;
  2. Aggiungere #include "A.h" all'inizio del B.cpp

Avviso tuttavia, non è possibile utilizzare in avanti dichiarazione per class A, la ragione è che si dispone di un valore b di class B come variabile membro della class A:

#pragma once 
#include "B.h" 
class A 
{ 
public: 
    A(); 
    void foo(); 
    void sayHello(); 
    B b;    /// I mean this line here more specifically 
}; 

La necessità compilatore per conoscere la definizione di class B al fine di determinare la dimensione corretta per la classe A. Ecco perché è necessario inserire lo #include "B.h" all'inizio di A.h.

+0

Non ho mai pensato di dichiarare solo una delle classi come quella nella parte superiore del file invece di dichiararla come una classe nella definizione della funzione. Sembra un po 'più pulito di quello che ho trovato dal momento che non ha bisogno della definizione per ogni volta che viene utilizzato. –

+0

Grazie! Questo ha funzionato per me. Non conoscevo ancora la dichiarazione anticipata :) – mrlux

1

Entrambe le intestazioni si riferiscono l'una all'altra. Solo uno può essere effettivamente valutato prima dal compilatore e non è possibile risolvere il riferimento alla classe nell'altra intestazione. Non è ovvio dall'errore, ma il #pragma "una volta" sta facendo in modo che uno degli header non avvenga come previsto.

Se il file di intestazione B.h non ha bisogno di conoscere i dettagli di implementazione della classe A, che in questo caso non include, non includere il file A.h in B.h. Invece, modificare la dichiarazione di funzione di bar() per assomigliare a questo:

bar(class A *a); 

Il compilatore può costruire il codice da questa che passa un puntatore ad un oggetto A senza sapere nulla di ciò che è dentro di A. E doesn' Ho bisogno dell'intestazione Ah.

Quindi includere il file di intestazione A.h dopo il file di intestazione B.h in B.cpp.

Ho provato questo e funziona per me.

+0

Ha funzionato per me per utilizzare una dichiarazione anticipata di A all'inizio di B.h. Qual è esattamente la differenza tra ciò e dichiarare la barra come hai fatto tu? Qual è la migliore pratica? – mrlux

+0

Non penso che ci sia una differenza significativa tra ciò che ho fatto sopra e gli altri suggerimenti. Il loro modo è migliore se si desidera evitare la digitazione aggiuntiva per più di uno di questi. –

2

Diamo uno sguardo a B.cpp e guardare cosa succede con il include:

#include "B.h" 

B::B(){} 
void B::bar(A *a) { 
    a->sayHello(); 
} 

Con Bh incollato al posto del includono otteniamo:

#include "A.h" 

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


B::B(){} 
void B::bar(A *a) { 
    a->sayHello(); 
} 

E poi mettendo in Ah luogo della sua includono:

#include "B.h" 
class A 
{ 
public: 
    A(); 
    void foo(); 
    void sayHello(); 
    B b; 
}; 

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


B::B(){} 
void B::bar(A *a) { 
    a->sayHello(); 
} 

E qui ci fermiamo perché pragma once impedisce ri-in clusione di B.h

Possiamo vedere che nella classe A abbiamo la definizione B b;, ma la classe B non è ancora definita. Kaboom. A non può essere definito senza B e B non è ancora stato definito.

A devono avere la dimensione di B per soddisfare B b;, quindi richiede la definizione completa di B. B ha bisogno di sapere solo A esiste perché ha bisogno solo di un puntatore a A per soddisfare void bar(A *a);. Quindi ...

A.h

#pragma once 
#include "B.h" 
class A 
{ 
public: 
    A(); 
    void foo(); 
    void sayHello(); 
    B b; 
}; 

A.cpp

#include "A.h" 
#include <iostream>  
A::A() {} 
void A::foo() { 
    b.bar(this); 
} 
void A::sayHello() { 
    std::cout << "Hello" << std::endl; 
} 

B.h

#pragma once 

class A; // forward definition of class A 
class B 
{ 
public: 
    B(); 
    void bar(A *a); 
}; 

B.cpp

#include "A.h" 
B::B(){} 
void B::bar(A *a) { 
    a->sayHello(); 
} 
+0

Grazie per la spiegazione dettagliata del perché sta accadendo. Lo sto capendo meglio ora :) – mrlux

+0

@mrlux Grazie per avermi dato la possibilità di scrivere questa risposta con intestazioni brevi ma significative. A proposito, una domanda ben composta. – user4581301

0

fare questi cambiamenti:

B.h : - Avanti dichiarare A

#pragma once 
// #include "A.h" 
class A; 
//^^^^^^ 

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

B.cpp: #include "A.h"

//#include "B.h" 
#include "A.h" 
//^^^^^^^^^^^^ 

B::B(){} 
void B::bar(A *a) { 
    a->sayHello(); 
} 

Questo è tutto.