Trovare il tipo di un oggetto in C ++


147

Ho una classe A e un'altra classe che eredita da essa, B. Sto sostituendo una funzione che accetta un oggetto di tipo A come parametro, quindi devo accettare un A. Tuttavia, in seguito chiamo funzioni che solo B ha, quindi voglio restituire false e non procedere se l'oggetto passato non è di tipo B.

Qual è il modo migliore per scoprire quale tipo di oggetto è passato alla mia funzione?

Risposte:


162

dynamic_cast dovrebbe fare il trucco

TYPE& dynamic_cast<TYPE&> (object);
TYPE* dynamic_cast<TYPE*> (object);

Il dynamic_cast parola chiave esegue il cast di un dato da un puntatore o tipo di riferimento a un altro, eseguendo un controllo di runtime per garantire la validità del cast.

Se si tenta di eseguire il cast al puntatore a un tipo che non è un tipo di oggetto reale, il risultato del cast sarà NULL. Se si tenta di eseguire il cast per fare riferimento a un tipo che non è un tipo di oggetto reale, il cast genererà bad_castun'eccezione.

Assicurarsi che vi sia almeno una funzione virtuale nella classe Base per far funzionare dynamic_cast.

Argomento di Wikipedia Informazioni sul tipo di runtime

RTTI è disponibile solo per le classi polimorfiche, il che significa che hanno almeno un metodo virtuale. In pratica, questo non è un limite perché le classi base devono avere un distruttore virtuale per consentire agli oggetti delle classi derivate di eseguire una corretta pulizia se vengono eliminati da un puntatore base.


1
Cosa vuoi dire con ci deve essere una funzione virtuale nella classe Base per far funzionare dynamic_cast. Mi sembra importante, che indovinerò.
GiCo,

3
OK l'ha trovato: le informazioni sul tipo di run-time (RTTI) sono disponibili solo per le classi polimorfiche, il che significa che hanno almeno un metodo virtuale. dynamic_cast e typeid necessitano di RTTI.
GiCo,

Non dynamic_castgetta se non è convertibile? C'è un modo per farlo senza generare un tiro?
1616

A* aptr = dynamic_cast<A*>(ptr);// non dovrebbe essere così
Mehdi Karamosly

Questo funziona per i POD che sono stati espressi in un uint8_t*? Cioè, posso verificarlo uint32_t* x = dynamic_cast<uint32_t*>(p), dove si ptrova uint8_t*? (Sto cercando di trovare un test per punire le violazioni).
1919

157

Il cast dinamico è il migliore per la tua descrizione del problema, ma voglio solo aggiungere che puoi trovare il tipo di classe con:

#include <typeinfo>

...
string s = typeid(YourClass).name()

4
Bene se davvero non sai quale sia il tuo oggetto. La risposta accettata presuppone che tu lo faccia.
unludo,

4
@xus Sì. fa parte delle intestazioni
standard

8
Io non vedo come. I nomi ID di tipo non devono essere utili e sono definiti dall'implementazione.
Scarpa

3
Più interessante qui: i nomi delle istanze della stessa classe non devono essere uguali. Tuttavia il typeid stessa deve confrontare uguale per le istanze della stessa classe, vedere stackoverflow.com/questions/1986418/typeid-versus-typeof-in-c
FourtyTwo

1
Nota gcc restituisce il nome magled ad es 11MyClass. Per districare è possibile utilizzare la libreria di estensioni ABI in cxxabi.h. Questo ti dà abi::__cxa_demangleche ti darà il vero nome
David G,

27

Questo si chiama RTTI , ma quasi sicuramente vuoi riconsiderare il tuo progetto qui, perché trovare il tipo e fare cose speciali basate su di esso rende il tuo codice più fragile.


4
Vero. Sfortunatamente sto lavorando a un progetto esistente, quindi non posso davvero andare a cambiare il design o qualcosa in classe A.
Lemmisca,

11

Per essere completo, costruirò il build-off di Robocide e farò notare che typeidpuò essere usato da solo senza usare name ():

#include <typeinfo>
#include <iostream>

using namespace std;

class A {
public:
    virtual ~A() = default; // We're not polymorphic unless we
                            // have a virtual function.
};
class B : public A { } ;
class C : public A { } ;

int
main(int argc, char* argv[])
{
    B b;
    A& a = b;

    cout << "a is B: " << boolalpha << (typeid(a) == typeid(B)) << endl;
    cout << "a is C: " << boolalpha << (typeid(a) == typeid(C)) << endl;
    cout << "b is B: " << boolalpha << (typeid(b) == typeid(B)) << endl;
    cout << "b is A: " << boolalpha << (typeid(b) == typeid(A)) << endl;
    cout << "b is C: " << boolalpha << (typeid(b) == typeid(C)) << endl;
}

Produzione:

a is B: true
a is C: false
b is B: true
b is A: false
b is C: false

9

Probabilmente incorporare nei tuoi oggetti un "tag" ID e usarlo per distinguere tra oggetti di classe A e oggetti di classe B.

Questo tuttavia mostra un difetto nel design. Idealmente quei metodi in B che A non ha, dovrebbero essere parte di A ma lasciati vuoti e B li sovrascrive. Questo elimina il codice specifico della classe ed è più nello spirito di OOP.



4

Perché la tua classe non è polimorfica. Provare:

struct BaseClas { int base; virtual ~BaseClas(){} };
class Derived1 : public BaseClas { int derived1; };

Ora BaseClasè polimorfico. Ho cambiato classe in struct perché i membri di una struct sono pubblici per impostazione predefinita.


3

La tua descrizione è un po 'confusa.

In generale, anche se alcune implementazioni in C ++ hanno meccanismi per questo, non dovresti chiedere del tipo. Invece, dovresti fare un Dynamic_cast sul puntatore ad A. Ciò che farà è che in fase di esecuzione, verrà verificato il contenuto effettivo del puntatore ad A. Se hai una B, otterrai il puntatore su B. In caso contrario, otterrai un'eccezione o null.


1
Va notato che otterrai un'eccezione solo se esegui un cast di riferimento che fallisce. vale a dire dynamic_cast <T &> (t). I lanci del puntatore non riusciti restituiscono NULL. vale a dire dynamic_cast <T *> (t)
AlfaZulu il

Sì, avrei dovuto chiarirlo meglio. Grazie. Vorrei che ci fosse una parola che descriva in tipi C che sono per riferimento piuttosto che per valore.
Uri,

3

Come altri hanno indicato, puoi usare dynamic_cast. Ma generalmente usare dynamic_cast per scoprire il tipo di classe derivata su cui stai lavorando indica la cattiva progettazione. Se si esegue l'override di una funzione che accetta il puntatore di A come parametro, dovrebbe essere in grado di lavorare con i metodi / i dati della stessa classe A e non dovrebbe dipendere dai dati della classe B. Nel proprio caso invece di sovrascrivere se si sei sicuro che il metodo che stai scrivendo funzionerà solo con la classe B, quindi dovresti scrivere un nuovo metodo nella classe B.


1

Utilizzare funzioni sovraccaricate. Non richiede dynamic_cast o nemmeno il supporto RTTI:

class A {};
class B : public A {};

class Foo {
public:
    void Bar(A& a) {
        // do something
    }
    void Bar(B& b) {
        Bar(static_cast<A&>(b));
        // do B specific stuff
    }
};

Proprio dalla domanda originale: "In seguito chiamerò funzioni che solo B ha" - come potrebbe funzionare il sovraccarico in questo caso?
Marcin Gil,

Quando chiami Bar con una A, non succede nulla di B. Quando si chiama Bar con una B, è possibile chiamare i metodi che esistono solo su B. Leggi la domanda originale? Bar è il suo "Sto sostituendo una funzione che accetta un oggetto di tipo A come parametro"
jmucchiello

7
Questo non funziona con il polimorfismo dinamico, che sospetto stia usando l'interrogatore. C ++ non può selezionare un sovraccarico in base alla classe di runtime del parametro, solo in base al tipo di tempo di compilazione.
Steve Jessop,

1

Se puoi accedere alla libreria boost, forse la funzione type_id_with_cvr () è ciò di cui hai bisogno, che può fornire il tipo di dati senza rimuovere i modificatori const, volatile e & & && . Ecco un semplice esempio in C ++ 11:

#include <iostream>
#include <boost/type_index.hpp>

int a;
int& ff() 
{
    return a;
}

int main() {
    ff() = 10;
    using boost::typeindex::type_id_with_cvr;
    std::cout << type_id_with_cvr<int&>().pretty_name() << std::endl;
    std::cout << type_id_with_cvr<decltype(ff())>().pretty_name() << std::endl;
    std::cout << typeid(ff()).name() << std::endl;
}

Spero che sia utile

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.