it-swarm.com.de

Wie überprüfe ich, ob der Typ eines Objekts eine bestimmte Unterklasse in C++ ist?

Ich dachte nach, typeid() zu verwenden, aber ich weiß nicht, wie ich fragen soll, ob dieser Typ eine Unterklasse einer anderen Klasse ist (die übrigens abstrakt ist).

53
Chad

Das solltest du wirklich nicht. Wenn Ihr Programm wissen muss, um welche Klasse es sich bei einem Objekt handelt, weist dies normalerweise auf einen Konstruktionsfehler hin. Prüfen Sie, ob Sie mit den virtuellen Funktionen das gewünschte Verhalten erzielen können. Weitere Informationen darüber, was Sie versuchen, könnten helfen.

Ich gehe davon aus, dass Sie eine Situation wie diese haben:

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

void foo(Base *p)
{
  if(/* p is A */) /* do X */
  else /* do Y */
}

Wenn es das ist, was Sie haben, dann versuchen Sie so etwas zu tun:

class Base
{
  virtual void bar() = 0;
};

class A : public Base
{
  void bar() {/* do X */}
};

class B : public Base
{
  void bar() {/* do Y */}
};

void foo(Base *p)
{
  p->bar();
}

Edit: Da die Debatte über diese Antwort nach so vielen Jahren immer noch andauert, dachte ich, ich sollte ein paar Hinweise geben. Wenn Sie über einen Zeiger oder einen Verweis auf eine Basisklasse verfügen und Ihr Code die abgeleitete Klasse des Objekts kennen muss, verstößt er gegen Liskov-Substitutionsprinzip . Onkel Bob nennt dies " Anathema zu objektorientiertem Design ".

36
Dima

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

class D1: public Base {};

class D2: public Base {};

int main(int argc,char* argv[]);
{
  D1   d1;
  D2   d2;

  Base*  x = (argc > 2)?&d1:&d2;

  if (dynamic_cast<D2*>(x) == nullptr)
  {
    std::cout << "NOT A D2" << std::endl;
  }
  if (dynamic_cast<D1*>(x) == nullptr)
  {
    std::cout << "NOT A D1" << std::endl;
  }
}
86
Martin York

Sie können dies mit dynamic_cast tun (zumindest für polymorphe Typen).

Auf den zweiten Blick kann man eigentlich nicht sagen, ob es sich um einen bestimmten Typ mit dynamic_cast-- handelt, aber man kann erkennen, ob es sich um diesen Typ oder eine Unterklasse handelt.

template <class DstType, class SrcType>
bool IsType(const SrcType* src)
{
  return dynamic_cast<const DstType*>(src) != nullptr;
}
21
Drew Hall

dynamic_cast kann bestimmen, ob der Typ den Zieltyp an einer beliebigen Stelle in der Vererbungshierarchie enthält (Ja, es ist ein wenig bekanntes Merkmal, dass, wenn B von A und C erbt, ein A* direkt in ein C* umgewandelt werden kann). typeid() kann den genauen Typ des Objekts bestimmen. Beide sollten jedoch äußerst sparsam eingesetzt werden. Wie bereits erwähnt, sollten Sie immer die dynamische Typidentifizierung vermeiden, da dies auf einen Konstruktionsfehler hinweist. (Wenn Sie wissen, dass das Objekt sicher vom Zieltyp ist, können Sie einen Downcast mit einem static_cast durchführen. Boost bietet ein polymorphic_downcast, der einen Downcast mit dynamic_cast und assert im Debug-Modus durchführt und im Freigabemodus nur verwendet a static_cast).

6
coppro

Ich weiß nicht, ob ich dein Problem richtig verstehe, also lass es mich mit meinen eigenen Worten wiederholen ...

Problem: Bestimmen Sie anhand der Klassen B und D, ob D eine Unterklasse von B ist (oder umgekehrt?).

Lösung: Verwenden Sie etwas Schablonenmagie! Okay, im Ernst, Sie müssen sich LOKI ansehen, eine exzellente Template-Meta-Programmierbibliothek, die vom berühmten C++ - Autor Andrei Alexandrescu erstellt wurde.

Genauer gesagt, laden Sie LOKI herunter und fügen Sie den Header TypeManip.h in Ihren Quellcode ein. Verwenden Sie dann die SuperSubclass-Klassenvorlage wie folgt:

if(SuperSubClass<B,D>::value)
{
...
}

Laut Dokumentation ist SuperSubClass<B,D>::value wahr, wenn B eine öffentliche Basis von D ist oder wenn B und D Aliase des gleichen Typs sind.

d. h. D ist eine Unterklasse von B oder D ist die gleiche wie B.

Ich hoffe das hilft.

edit:

Bitte beachten Sie, dass die Auswertung von SuperSubClass<B,D>::value zur Kompilierzeit erfolgt, im Gegensatz zu einigen Methoden, die dynamic_cast verwenden. Daher gibt es keine Nachteile für die Verwendung dieses Systems zur Laufzeit.

4
Autodidact

Ich stimme nicht zu, dass Sie niemals den Typ eines Objekts in C++ prüfen möchten. Wenn Sie es vermeiden können, stimme ich zu, dass Sie dies tun sollten. Zu sagen, dass Sie dies NIEMALS tun sollten, ist jedoch zu weit. Sie können dies in sehr vielen Sprachen tun und es kann Ihnen das Leben erleichtern. Howard Pinsley hat uns beispielsweise gezeigt, wie er in seinem Beitrag zu C # arbeitet. 

Ich arbeite viel mit dem Qt Framework. Im Allgemeinen modelliere ich, was ich nach der Art und Weise mache, wie sie Dinge tun (zumindest wenn sie in ihrem Rahmen arbeiten). Die QObject-Klasse ist die Basisklasse aller Qt-Objekte. Diese Klasse hat die Funktionen isWidgetType () und isWindowType () als schnelle Unterklassenprüfung. Warum sollten Sie also nicht in der Lage sein, Ihre eigenen abgeleiteten Klassen zu überprüfen, die ihrer Natur nach vergleichbar sind? Hier ist ein QObject-Spin off von einigen dieser anderen Beiträge:

class MyQObject : public QObject
{
public:
    MyQObject( QObject *parent = 0 ) : QObject( parent ){}
    ~MyQObject(){}

    static bool isThisType( const QObject *qObj )
    { return ( dynamic_cast<const MyQObject*>(qObj) != NULL ); }
};

Wenn Sie dann einen Zeiger auf ein QObject übergeben, können Sie überprüfen, ob es auf Ihre abgeleitete Klasse verweist, indem Sie die statische Memberfunktion aufrufen:

if( MyQObject::isThisType( qObjPtr ) ) qDebug() << "This is a MyQObject!";
4
BuvinJ

In c # kann man einfach sagen:

if (myObj is Car) {

}
3
Howard Pinsley
#include <stdio.h>
#include <iostream.h>

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

  template<typename T>
  bool isA() {
    return (dynamic_cast<T*>(this) != NULL);
  }
};

class D1: public Base {};
class D2: public Base {};
class D22: public D2 {};

int main(int argc,char* argv[]);
{
  D1*   d1  = new D1();
  D2*   d2  = new D2();
  D22*  d22 = new D22();

  Base*  x = d22;

  if( x->isA<D22>() )
  {
    std::cout << "IS A D22" << std::endl;
  }
  if( x->isA<D2>() )
  {
    std::cout << "IS A D2" << std::endl;
  }
  if( x->isA<D1>() )
  {
    std::cout << "IS A D1" << std::endl;
  }
  if(x->isA<Base>() )
  {
    std::cout << "IS A Base" << std::endl;
  }
}

Ergebnis:

IS A D22
IS A D2
IS A Base
2
Reinaldo Guedes

Sie können dies nur zum Kompilieren mit Vorlagen tun, es sei denn, Sie verwenden RTTI. 

Damit können Sie die typeid-Funktion verwenden, die einen Zeiger auf eine type_info-Struktur ergibt, die Informationen zum Typ enthält.

Lesen Sie es unter Wikipedia

1
user32141

Ich dachte über die Verwendung von typeid()... nach.

Nun, ja, es könnte durch einen Vergleich gemacht werden: typeid().name(). Wenn wir die bereits beschriebene Situation annehmen, wo:

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

void foo(Base *p)
{
  if(/* p is A */) /* do X */
  else /* do Y */
}

Eine mögliche Implementierung von foo(Base *p) wäre:

#include <typeinfo>

void foo(Base *p)
{
    if(typeid(*p) == typeid(A))
    {
        // the pointer is pointing to the derived class A
    }  
    else if (typeid(*p).name() == typeid(B).name()) 
    {
        // the pointer is pointing to the derived class B
    }
}
0
Ziezi

Der folgende Code zeigt drei verschiedene Möglichkeiten:

  • virtuelle Funktion
  • typid
  • dynamic_cast
#include <iostream>
#include <typeinfo>
#include <typeindex>

enum class Type {Base, A, B};

class Base {
public:
    virtual ~Base() = default;
    virtual Type type() const {
        return Type::Base;
    }
};

class A : public Base {
    Type type() const override {
        return Type::A;
    }
};

class B : public Base {
    Type type() const override {
        return Type::B;
    }
};

int main()
{
    const char *typemsg;
    A a;
    B b;
    Base *base = &a;             // = &b;    !!!!!!!!!!!!!!!!!
    Base &bbb = *base;

    // below you can replace    base    with  &bbb    and get the same results

    // USING virtual function
    // ======================
    // classes need to be in your control
    switch(base->type()) {
    case Type::A:
        typemsg = "type A";
        break;
    case Type::B:
        typemsg = "type B";
        break;
    default:
        typemsg = "unknown";
    }
    std::cout << typemsg << std::endl;

    // USING typeid
    // ======================
    // needs RTTI. under gcc, avoid -fno-rtti
    std::type_index ti(typeid(*base));
    if (ti == std::type_index(typeid(A))) {
        typemsg = "type A";
    } else if (ti == std::type_index(typeid(B))) {
        typemsg = "type B";
    } else {
        typemsg = "unknown";
    }
    std::cout << typemsg << std::endl;

    // USING dynamic_cast
    // ======================
    // needs RTTI. under gcc, avoid -fno-rtti
    if (dynamic_cast</*const*/ A*>(base)) {
        typemsg = "type A";
    } else if (dynamic_cast</*const*/ B*>(base)) {
        typemsg = "type B";
    } else {
        typemsg = "unknown";
    }
    std::cout << typemsg << std::endl;
}

Das obige Programm gibt Folgendes aus:

type A
type A
type A
0
ajneu