2011-09-12 20 views
11

Sappiamo che possiamo risolvere il problema dei diamanti usando l'ereditarietà virtuale.In che modo il compilatore risolve internamente il problema dei diamanti in C++?

Ad esempio:

class Animal // base class 
    { 
    int weight; 
    public: 
    int getWeight() { return weight;}; 
    }; 
    class Tiger : public Animal { /* ... */ }; 
    class Lion : public Animal { /* ... */ }; 
    class Liger : public Tiger, public Lion { /* ... */ }; 
    int main() 
    { 
    Liger lg ; 
    /*COMPILE ERROR, the code below will not get past 
    any C++ compiler */ 
    int weight = lg.getWeight(); 
    } 

Quando compiliamo questo codice otterremo un errore di ambiguità. Ora la mia domanda è come il compilatore rileva internamente questo problema di ambiguità (problema dei diamanti).

+1

Nota che non si utilizza l'ereditarietà virtuale nel codice. La tua descrizione sembra implicare che l'esempio riguarda la soluzione del problema dei diamanti usando l'ereditarietà virtuale. –

+3

Ho votato solo perché il "Liger" mi ha fatto ridere :-) – selalerer

+1

Ho sempre pensato che il problema del diamante fosse la stampa di un diamante sullo schermo di una determinata dimensione ... mi ci volle un minuto per intendere conflitto di ereditarietà ... – corsiKa

risposta

4

Il compilatore crea tabelle che elencano tutti i membri di ogni classe e ha anche collegamenti che consentono di andare su e giù per la catena di ereditarietà per qualsiasi classe.

Quando è necessario individuare una variabile membro (peso nell'esempio), il compilatore inizia dalla classe effettiva, nel caso Liger. Non troverà un membro del peso lì, quindi si sposta di un livello fino alla classe genitore. In questo caso ce ne sono due, quindi esegue la scansione sia di Tiger che di Lion per un membro di nome weight. Non ci sono ancora colpi, quindi ora ha bisogno di salire di un altro livello, ma deve farlo due volte, una volta per ogni classe a questo livello. Questo continua fino a quando il membro richiesto non viene trovato a un certo livello dell'albero di ereditarietà. Se in un dato livello trova un solo membro che considera tutti i rami multipli di ereditarietà, tutto va bene, se trova due o più membri con il nome richiesto, allora non può decidere quale scegliere in modo che commetta errori.

3

Quando il compilatore crea una tabella di puntatori di funzione per una classe, ogni simbolo deve apparire esattamente una volta. In questo esempio, getWeight viene visualizzato due volte: in Tiger e in Lion (perché lo Liger non lo implementa, quindi sale nell'albero per cercarlo), quindi il compilatore si blocca.

È piuttosto semplice, in realtà.

+0

L'errore si verifica solo quando viene chiamato il metodo, non quando la classe è definita. Il compilatore non si preoccupa se il nome del metodo + firma appare due volte finché non c'è chiamata a questo metodo. – selalerer

+0

@sela, vero, il compilatore ignora i metodi che non vengono chiamati. Il compilatore crea solo la tabella per i metodi effettivamente utilizzati. Un buon compilatore, in ogni caso, non sa se è richiesto per specifica. – littleadv

+1

-1, i compilatori di solito non producono tabelle di puntatori di funzione. Soprattutto non "solo i metodi usati". Questo non si confonde con la compilazione separata; l'insieme di metodi usati di solito differisce per unità di traduzione. La più vicina è la tabella di esportazione nei file oggetto. Tuttavia, questo non contiene una voce 'Liger :: getWeight'. Quindi questo non risponde affatto alla domanda. – MSalters

1

Il compilatore cerca getWeight in Liger, non ne trova, quindi controlla i suoi genitori e i genitori dei suoi genitori e così via e così via, se ne trova più di uno, restituisce un errore e muore su di te, perché non può dire quale dovrebbe essere usato.

+2

Solo quando un simbolo viene trovato più di una volta allo stesso livello di ereditarietà c'è un'ambiguità. In generale, l'ambiguità può essere risolta qualificando la chiamata; tuttavia, nel caso particolare del problema del diamante, non è possibile poiché entrambi i simboli provengono dalla stessa classe. –

+2

@MMavipc Il valore restituito non fa parte della firma di un metodo. Il compilatore non esamina il valore restituito quando decide quale metodo chiamare. – selalerer

+0

@selalerer derp, grazie per averlo indicato. – Avery3R

1

con il codice, la struttura per liger è

Liger[Tiger[Animal]Lion[Animal]] 

Se si chiama una funzione Animal da un puntatore Liger, ci sono in realtà due animali un Liger può convertire (da qui l'ambiguità)

eredità virtuale genererà una struttura come

Liger[Tiger[*]Lion[Animal]] 
      \-----/ 

V'è ora un solo animale, indirettamente, raggiungibile da entrambe le basi, quindi una conversione da Liger ad Animal è più ambigua.