2011-09-07 13 views
5

Date le classi inferiori sono in due file di intestazione separati e possono apparire in qualsiasi ordine:classi C++ prototipo conflitto

//TestB.h 
class TestB; //Forward declaration for a later operator= in a centralised header 

class TestA 
{ 
    const TestA&operator=(const TestB); //defined in Test.h 
}; 

E:

//TestA.h 
class TestA; //Forward declaration for a later operator= in a centralised heaer 

class TestB 
{ 
    const TestB&operator=(const TestA); //defined in Test.h 
}; 

Come devo fare per evitare il conflitto prototipo?

L'aiuto è apprezzato.

Chiedo scusa a tutti! Avevo intenzione di fare riferimento a riferimenti (e commerciali nell'operatore = argomenti - non avrei mai superato copiando semplici POD) e intendevo solo la questione dei conflitti di prototipazione! Immagino che vada a dimostrare l'importanza della correzione di bozze! Ho accettato la risposta dato il contesto originale (il mio errato).

Mi ero semplicemente voltato per alcuni minuti e non ero a conoscenza della colpa!

+1

Cosa intendi per "conflitto prototipo"? –

+2

Qui non c'è niente di sbagliato: funzionerebbe bene. Dovrai spiegare meglio l'errore e con codice reale. –

+1

@Als: è possibile dichiarare funzioni che assumono tipi incompleti in base al valore; non puoi semplicemente definirli o chiamarli fino a dopo la definizione del tipo. –

risposta

4

Si passano i riferimenti alle classi come parametri. In questo modo, una classe e le sue funzioni membro possono essere dichiarate senza conoscere l'altra.

//TestB.h 
class TestB; //Forward declaration for a later operator= in a centralised header 

class TestA 
{ 
    const TestA&operator=(const TestB &); //defined in TestB.h 
}; 

E:

//TestA.h 
class TestA; //Forward declaration for a later operator= in a centralised heaer 

class TestB 
{ 
    const TestB&operator=(const TestA *); //defined in TestA.h 
}; 

Dopo questo, si dovrà includere sia TestA.h e TestB.h in entrambi i file TestA.cpp e TestB.cpp per essere in grado di definire tali Stati funzioni.

+0

Non c'è bisogno di cambiare la semantica, puoi ** dichiarare funzioni che prendono o restituiscono un * tipo incompleto * in base al valore finché non provi a * define * o * chiama * quelle funzioni. –

4

La mia risposta originale sembra completamente sbagliata.

Verificare di avere protezioni incluse in tutti i file di intestazione in modo da non finire con una catena di inclusione infinita. Quindi includere le intestazioni in ogni implementazione:

// A.cpp 
#include "A.h" 
#include "B.h" // for complete type TestB 

const TestA & TestA::operator=(const TestB) { /* ... */ } 

// B.cpp 
#include "B.h" 
#include "A.h" // for complete type TestA 

const TestB & TestB::operator=(const TestA) { /* ... */ } 

Si prega di notare che una tale costruzione crea la situazione di progetto curioso in cui tutti i consumatori di entrambi TestA o TestB che ha voluto chiamare l'operatore deve sempre includere sia A.h e B.h, che è molto granulare, ma anche un po 'inaspettato. Potrebbe essere utile aggiungere un file di intestazione intermedio per l'utilizzo da parte del client che include entrambi i file di intestazione o per aggiungere inclusioni reciproche (con guardie!) Ai file di intestazione stessi.


Non si può risolvere questo nel modo in cui lo ha scritto, perché si ha un ricorsivo dipendenza infinita flat-out.

Il modo in cui di solito si esegue questa operazione è quello di passare gli argomenti per riferimento piuttosto che per copia, in quanto il passaggio per riferimento fa non richiedono la conoscenza del tipo completo:

const TestA & operator=(const TestB &); 
            ^^^^^ 
            vvvvv 
const TestB & operator=(const TestA &); 

+2

È possibile dichiarare le funzioni prendendo gli argomenti in base al valore anche se il tipo di argomento è incompleto. Non è possibile definire la funzione fino a dopo la definizione della classe. –

+0

@Mike: Sono stupito, ho davvero pensato che non fosse permesso! –

+0

@Kerrek SB: non è nemmeno necessario aggiungere gli include ciclici purché le funzioni non siano * definite * o * chiamate * nei file di intestazione e che le unità di traduzione che * chiamano * o * definiscano * tali membri le funzioni includono entrambe le intestazioni. –

0

Le dichiarazioni di inoltro sono utili solo per dichiarare puntatori e riferimenti al tipo ... non possono essere utilizzati per le effettive operazioni di copia e le dichiarazioni delle istanze di classe come membri di dati di classe non statici. Se provi a farlo, il compilatore ti darà un errore per un tipo incompleto, poiché non ci sono abbastanza informazioni presenti per fornire al compilatore informazioni su cosa sia il tipo reale composto (cioè, quanto è grande, ecc.), in modo da poter costruire la classe principale o determinare la quantità di spazio di stack da allocare a uno degli argomenti del metodo della classe.

1

Non c'è alcun problema reale in quelle intestazioni, purché esse solo dichiarino le funzioni membro e non forniscano definizioni. Cioè, se le definizioni per le funzioni membro sono in un file .cpp che include entrambe le intestazioni e che non ci sono chiamate a nessuna funzione nelle intestazioni, dovrebbe funzionare perfettamente.

C'è un malinteso comune che non è possibile utilizzare qualsiasi cosa che assomiglia a valore con un tipo in avanti dichiarato, il fatto è che non è possibile creare oggetti di quel tipo o creare variabili membro di quel tipo, ma si può dichiarare funzioni che prendono o restituiscono quei tipi dal valore.

Non è possibile, invece, definire o chiamata quelle funzioni, poiché ciò richiede la creazione di un oggetto del tipo incompleto.