2012-03-05 12 views
5

ho la seguente gerarchia:downcasting dal puntatore base ai tipi di derivati ​​su modelli

class base 
{ 
public: 
    virtual ~base(){} 
    virtual void foo() {} 
}; 

template <typename T> 
class derived1 : public base 
{ 
    virtual void foo() {}; 
}; 

template <typename T> 
class derived2 : public base 
{ 
    virtual void foo() {}; 
}; 

Ora dato un puntatore alla base, mi piacerebbe sapere se il sottostante è sia derived1 o derived2. Il problema è che sia derivato1 che derivato2 possono essere specializzati su molti diversi tipi , l'uso di dynamic_cast per testare un cast down richiede che il tipo di modello sia noto. Ho finito con po 'disordinato, unmaintable e incompleta di codice:

base* b = new derived1<int>(); 

if (dynamic_cast<derived1<int>*> || 
    dynamic_cast<derived1<unsigned int>*> || 
    dynamic_cast<derived1<double>*>) 
    std::cout << "is derived1"; 
else if (dynamic_cast<derived2<int>*> || 
    dynamic_cast<derived2<unsigned int>*> || 
    dynamic_cast<derived2<double>*>) 
    std::cout << "is derived2"; 

Esiste un modo migliore, in grado di gestire qualsiasi tipo di specializzazione?

+2

Cosa intendi fare una volta che sai che il tipo è una specializzazione di uno dei modelli derivati? –

+0

@James: chiama una funzione specifica per ognuna - btw c'è più di derivata1 e derivata2 –

+6

Il codice condizionale che deve verificare dinamicamente il tipo concreto di un puntatore di classe base è un odore di codice errato. C'è probabilmente un modo per cambiare il tuo design per evitare questo. –

risposta

8

Sposta la logica che dipende dal tipo nel tipo.

Invece di:

if (dynamic_cast<derived1<int>*>(b) || 
    dynamic_cast<derived1<unsigned int>*>(b) || 
    dynamic_cast<derived1<double>*>(b)) 
    std::cout << "is derived1"; 
else if (dynamic_cast<derived2<int>*>(b) || 
    dynamic_cast<derived2<unsigned int>*>(b) || 
    dynamic_cast<derived2<double>*>(b)) 
    std::cout << "is derived2"; 

aggiungere una funzione di virtual print_name() const-base, e poi fare:

void example() { 
    std::unique_ptr<base> b(new derived1<int>()); 
    b->print_name(); 
} 
class base 
{ 
public: 
    ~base(){} 
    virtual void foo() {} 
    virtual void print_name() const = 0; 
}; 

template <typename T> 
class derived1 : public base 
{ 
    virtual void foo() {} 
    virtual void print_name() const { 
     std::cout << "is derived1"; 
    } 
}; 

template <typename T> 
class derived2 : public base 
{ 
    virtual void foo() {} 
    virtual void print_name() const { 
     std::cout << "is derived2"; 
    } 
}; 
6

Inserire una classe non su modelli inbetween base e derived1 o derived2:

class base 
{ 
public: 
    virtual ~base() {} // **NOTE** Should be virtual 
    virtual void foo() {} 
}; 

class derived1_base : public base 
{ 
}; 

template <typename T> 
class derived1 : public derived1_base 
{ 
public: 
    virtual void foo() {} 
}; 

class derived2_base : public base 
{ 
}; 

template <typename T> 
class derived2 : public derived2_base 
{ 
public: 
    virtual void foo() {} 
}; 

In un commento è menzionato:

[Voglio] chiamare una funzione particolare per ciascuno di essi - btw c'è più di derived1 e derived2

Aggiungi quella funzione (virtuale) per derived1_base, e non hai nemmeno bisogno di sapere più T.

if (dynamic_cast<derived1_base*>(foo)) 
{ 
    std::cout << "is derived1"; 
    dynamic_cast<derived1_base*>(foo)->specific_derived1_function(); 
} 
else if (dynamic_cast<derived2_base*>(foo)) 
{ 
    std::cout << "is derived2"; 
    dynamic_cast<derived2_base*>(foo)->specific_derived2_function(); 
} 

NOTA: ritengo un elenco di dynamic_cast<> un codice odore, e vi esorto a ripensare il vostro approccio.

2

Soluzione 1: aggiungere più una funzione virtuale:

enum DerivedType 
{ 
    One, 
    Two, 
    ... 
}; 

class base 
{ 
public: 
    ~base(){} 
    virtual void foo() {} 
    virtual DerivedType GetType() = 0; 
}; 

template <typename T> 
class derived1 : public base 
{ 
    virtual void foo() {}; 
    virtual DerivedType GetType() { return One; } 
}; 

template <typename T> 
class derived2 : public base 
{ 
    virtual void foo() {}; 
    virtual DerivedType GetType() { return Two; } 
}; 

Soluzione 2: utilizzando le classi tag:

class Base 
{ 
public: 
    virtual ~Base() { } 
}; 

class Derived1Tag 
{ }; 

class Derived2Tag 
{ }; 

template <class T> 
class Derived1 : public Base, public Derived1Tag 
{ }; 

template <class T> 
class Derived2 : public Base, public Derived2Tag 
{ }; 


int main(int argc, char** argv) 
{ 
    Derived1<int> d1; 
    Derived2<int> d2; 

    cout << dynamic_cast<Derived1Tag*>((Base*)&d1) << endl; 
    cout << dynamic_cast<Derived1Tag*>((Base*)&d2) << endl; 

    return 0; 
} 
+0

Devo dire che @EmileCormier ha ragione e che questo ** probabilmente ** riflette una cattiva scelta di design da qualche parte. Se la funzione che vuoi eseguire dipende dal tipo, probabilmente dovrebbe essere una funzione virtuale dall'inizio. –

3

Si potrebbe aggiungere un metodo virtuale per fare una meta-tipo di controllo di qualche tipo :

class base 
{ 
public: 
    ~base(){} 
    virtual void foo() {} 
    virtual bool isa(const char* type_to_test){ 
      return strcmp(type_to_test,"base")==0;} 
}; 

template <typename T> 
class derived1 : public base 
{ 
    virtual void foo() {}; 
    virtual bool isa(const char* type_to_test){ 
    return strcmp(type_to_test,"derived1")==0;} 
}; 
Problemi correlati