2010-03-01 14 views
6

Come si verifica in fase di esecuzione se un oggetto è di tipo ClasseA o di tipo derivato ClassB? In un caso, devo gestire entrambi i casi separatamenteVerifica il tipo derivato (C++)

ClassA* SomeClass::doSomething (ClassA*) 
{ 
    if(/* parameter is of type base class */) { 

    } else if { /* derived class */) { 

    } 
} 

Forse potrei dire che la classe derivata ClassB ha alcune capacità speciali. Ma come posso farlo senza modificare la classe ClassA esistente?

+2

ClassA non dovrebbe aver bisogno di sapere nulla di ClassB. –

+0

Il controllo dei tipi di classe, in generale, è un indicatore di una progettazione errata. Vedi il modello di design * Visitor * per un'alternativa. Studia anche sul polimorfismo. –

risposta

20

In genere è una pessima idea attivare il tipo esatto in questo modo. In questo modo, si accoppia strettamente il metodo alle classi derivate di ClassA. Dovresti usare il polimorfismo. Introdurre un metodo virtual nella classe A, sovrascriverlo nella classe B e chiamarlo semplicemente nel metodo.

Anche se sono stato costretto a gestire la funzionalità della stessa funzione esterna per qualche motivo, vorrei fare qualcosa di simile:

class ClassA { 
    public: virtual bool hasSpecificFunctionality() { return false; } 
}; 

class ClassB : public ClassA { 
    public: virtual bool hasSpecificFunctionality() { return true; } 
}; 

ClassA* SomeClass::doSomething (ClassA* arg) 
{ 
    if (arg->hasSpecificFunctionality()) { 

    } else { 

    } 
} 
+0

+1 - ma lui dice che non vuole/non può cambiare ClassA, quindi una funzione virtuale è fuori questione. –

+2

@Poita_: se non riesci a cambiare la classe base, forse dovresti essere andato con la composizione invece dell'ereditarietà in primo luogo. –

+0

E se avesse bisogno di polimorfismo dalla classe base ...? –

4

Perché non utilizzare un metodo doSomething() su ClassB che gestisce le funzionalità extra di ClassB? Questa è l'essenza del polimorfismo.

+2

No, il polimorfismo richiede di avere il metodo 'doSomething' su' ClassA' (possibilmente come un noop), sovrascrivendolo in 'ClassB'. L'esempio mostra anche perché: non puoi chiamare 'ClassB :: doSomething()' su un 'ClassA *'. – MSalters

2

slighlty diverso da quello che avete chiesto

ClassB* b; 
if ((b = dynamic_cast<ClassB*>(ptr)) == 0) { 
    // not a classB* 
} else { 
    // a classB* in b 
} 
3

La sintassi è la seguente:

ClassA* SomeClass::doSomething (ClassA* pa) 
{ 
    ClassB* pb = dynamic_cast<ClassB*>(pa); 
    if(pb) ... 

(Si noti che questo funziona solo all'interno di gerarchie di classi polimorfiche Cioè, ci devono essere le funzioni virtuali. coinvolto.)

Tuttavia, si dovrebbe cercare di evitarlo. Cosa ti serve per quello che non può essere risolto applicando funzioni virtuali?

12

Utilizzare un dynamic_cast come segue:

ClassA* SomeClass::doSomething(ClassA *a) 
{ 
    if (dynamic_cast<DerivedClass *>(a)) { 
     .... 
    } else if (dynamic_cast<BaseClass *>(a)) { 
     .... 
    } 
} 

dynamic_cast<T *>(ptr) volontà restituire 0 nel caso ptr non è un puntatore del tipo T e restituirà un puntatore di tipo T in caso contrario.

dynamic_cast può essere evitato ed è un indicatore di progettazione/codice errato. Se puoi evitarlo, prova a farlo, in quanto richiede RTTI nel tuo eseguibile finale.

+4

Ma ricordate che l'argomento su dynamic_cast deve essere un puntatore (o riferimento) a una classe polimorfica (vale a dire una classe con almeno un metodo virtuale). Altrimenti dynamic_cast fallirà. –

+2

Dovrai provare il cast in "DerivedClass' prima del cast in' BaseClass', poiché il cast di 'BaseClass' riuscirà per entrambi i tipi. –

+0

Buon punto :) Ho modificato la risposta. –

2

Altri hanno sottolineato che l'attivazione dei tipi è in genere una cattiva idea, quindi non lo farò. Se avete veramente farlo, è possibile utilizzare l'operatore typeid per accendere il tipo dinamico di un oggetto:

ClassA* SomeClass::doSomething (ClassA* a) 
{ 
    if (typeid(*a) == typeid(ClassA)) { 
     /* parameter is of type base class */ 
    } else if (typeid(*a) == typeid(ClassB)) { 
     /* a specific derived class */ 
    } else { 
     /* some other derived class */ 
    } 
} 

dynamic_cast è simile, ma i test per la convertibilità, non uguaglianza:

ClassA* SomeClass::doSomething (ClassA* a) 
{ 
    if (ClassB *b = dynamic_cast<classB*>(a)) { 
     /* parameter is, or is derived from, ClassB */ 
    } else { 
     /* parameter is, or is derived from, ClassA but not ClassB */ 
    } 
} 

Funzionano solo se ClassA è polimorfico (ovvero ha almeno una funzione virtuale).

Problemi correlati