2012-06-20 12 views
5

Sto cercando di scoprire la classe più derivata di un oggetto, all'interno di un costruttore di una delle classi nel suo albero di ereditarietà. Ho passato diverse ore su questo ora e sono in perdita per come altro posso farlo o perché non ha senso. Sembra avere perfettamente senso, eppure si rifiuta di lavorare. Ho trovato numerose pagine su RTTI e praticamente non sono stato in nessun posto con loro. Continuerò a spiegare dopo il mio caso di test e il suo output.Il puntatore "questo" è abilitato per RTTI?

La fonte:

#include <iostream> 
#include <typeinfo> 
#include <string> 

class A 
{ 
public: 
    A(std::string foo); 

    virtual void bar(A* a) = 0; 
}; 

class B : public A 
{ 
public: 
    B(); 

    virtual void bar(A* a); 
}; 

A::A(std::string foo) 
{ 
    std::cout << "type as passed to A constructor: " << foo << " (" << this << ")" << std::endl; 
    std::cout << "type as determined in A constructor: " << typeid(*this).name() << " (" << this << ")" << std::endl; 
} 

B::B() : A(typeid(*this).name()) 
{ 
    A* a = (A*)this; 
    std::cout << "type as determined in B constructor: " << typeid(*a).name() << " (" << this << ")" << std::endl; 
    this->bar(this); 
} 

void B::bar(A* a) 
{ 
    std::cout << "type as determined in bar: " << typeid(*a).name() << " (" << a << ")" << std::endl; 
} 

int main() 
{ 
    B b; 
    b.bar(&b); 

    return 0; 
} 

L'uscita (in g ++):

type as passed to A constructor: 1B (0x7fff5fbff910) 
type as determined in A constructor: 1A (0x7fff5fbff910) 
type as determined in B constructor: 1B (0x7fff5fbff910) 
type as determined in bar: 1B (0x7fff5fbff910) 
type as determined in bar: 1B (0x7fff5fbff910) 

sto cercando di ottenere la seconda riga dell'output dire "1B" invece di "1A" . Il RTTI è stato rimosso da "questo" per qualche motivo che non riesco ancora a immaginare? In che modo non infrange l'idea delle funzioni virtuali? (L'ho implementato con funzioni virtuali fino a quando ho scoperto che stavo reimplementando parte di RTTI, che non avevo mai conosciuto prima.) Come mostra l'output, posso farlo funzionare se evito di usare "this", ma il bisogno farlo sembrerebbe una rottura del design.

+2

perché stai cercando di rilevare il tipo? non è buono questo suona come una soluzione (imperfetta) tentata a qualcos'altro, qual'è qualcos'altro? –

+0

Alf, ho std :: mappe di tipi (std :: stringhe, in questo caso) per oggetti, con ogni tipo che ha un oggetto unico all'interno della mappa. Ogni istanza di una determinata classe ha una propria mappa, per consentire di aggiungere facilmente nuovi oggetti a questi oggetti core e successivamente recuperati per tipo. (Lo stavo facendo con i const e una rete di metodi virtuali prima che sapessi di RTTI, con un errore equivalente per lo stesso motivo.) – Grault

risposta

4

Non puoi farlo perché hai frainteso le dinamiche coinvolte.

La regola è:

Il this nel costruttore/distruttore di tutti i punti di classe alla classe il cui costruttore/distruttore che è.

Inoltre, questo è il motivo virtual funzioni chiamate nel costruttore non si comportano come ci si aspetterebbe una funzione virtual a lavorare quando si utilizza la spedizione dinamica.

Si dovrebbe rilevare il tipo , dopo il costruttore è stato called.You può farlo in una qualsiasi delle funzioni membro, tranne il costruttore e il distruttore.

+0

OK, ma perché è considerato migliore? Sembra non intuitivo nel migliore dei casi, per me. Romperebbe qualche altra funzionalità OO? – Grault

0

Considerate la relazione di ereditarietà, dovreste già sapere che il costruttore di A verrà eseguito prima e poi quello di B. All'interno del costruttore di A, l'oggetto non è ancora un B in quanto non è stato creato. Quindi, il tipo dinamico di un oggetto all'interno di un costruttore è sempre il tipo della classe stessa e mai il tipo di una classe derivata. Lo stesso succede nei distruttori.

In poche parole, non è possibile eseguire ciò che si desidera fare in fase di costruzione.

+0

"il costruttore di A verrà eseguito prima e poi quello di B". Questo è quello che ho pensato fino a quando non sono riuscito a passare "questo" come B nel costruttore A (che è dimostrato implicitamente nel caso di test). Ora capisco il flusso come il costruttore più derivato che esegue per primo, e ogni costruttore chiama il suo genitore prima di fare qualsiasi altra cosa. Così i corpi scorrono dalla radice in basso come dici tu. – Grault

3

Si sta visualizzando il comportamento previsto. Nel corpo di un costruttore il tipo dell'oggetto in costruzione è uguale al tipo della classe il cui costruttore viene eseguito e non la classe della classe derivata più costruita.

L'anomalia si trova in realtà nell'espressione typeid(*this) quando utilizzata in un'espressione di inizializzatore membro. Questo comportamento era non definito in C++ 03 ma questo è stato modificato in C++ 11 in modo da ottenere il tipo di classe del costruttore piuttosto che il tipo dell'oggetto reale che deve ancora essere costruito.

Problemi correlati