Ich habe typeid()
nach dem Prinzip der Verwendung gedacht, aber ich weiß nicht, wie ich fragen soll, ob dieser Typ eine Unterklasse einer anderen Klasse ist (die übrigens abstrakt ist).
Antworten:
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. Überprüfen Sie, ob Sie mithilfe virtueller Funktionen das gewünschte Verhalten erzielen können. Außerdem würden weitere Informationen darüber, was Sie versuchen, 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 Sie dies haben, versuchen Sie Folgendes:
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();
}
Bearbeiten: Da die Debatte über diese Antwort nach so vielen Jahren immer noch andauert, dachte ich, ich sollte einige Referenzen einbringen. Wenn Sie einen Zeiger oder Verweis auf eine Basisklasse haben und Ihr Code die abgeleitete Klasse des Objekts kennen muss, verstößt er gegen das Liskov-Substitutionsprinzip . Onkel Bob nennt dies ein " Anathema für objektorientiertes Design ".
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;
}
}
dynamic_cast<>
hier? Wäre das nicht static_cast<>
genug?
x
beim Kompilieren angeben? Wenn ja, dann static_cast<>()
würde es funktionieren. Wenn Sie die Art der x
bis zur Laufzeit nicht sagen können, dann brauchen Siedynamic_cast<>()
Sie können es mit tun dynamic_cast
(zumindest für polymorphe Typen).
Beim zweiten Gedanken - Sie können nicht sagen, ob es sich SPEZIELL um einen bestimmten Typ dynamic_cast
handelt - können Sie jedoch feststellen, ob es sich um diesen Typ oder eine Unterklasse davon handelt.
template <class DstType, class SrcType>
bool IsType(const SrcType* src)
{
return dynamic_cast<const DstType*>(src) != nullptr;
}
std::is_polymorphic_v<T>
ist false
.
Der folgende Code zeigt drei verschiedene Möglichkeiten:
#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 druckt Folgendes:
type A
type A
type A
dynamic_cast
kann bestimmen, ob der Typ den Zieltyp irgendwo in der Vererbungshierarchie enthält (ja, es ist eine wenig bekannte Funktion, die, wenn er B
von A
und erbt C
, einen A*
direkt in einen verwandeln kann C*
). typeid()
kann den genauen Typ des Objekts bestimmen. Beide sollten jedoch äußerst sparsam eingesetzt werden. Wie bereits erwähnt, sollten Sie die dynamische Typidentifikation immer vermeiden, da dies auf einen Konstruktionsfehler hinweist. (Wenn Sie wissen, dass das Objekt sicher vom Zieltyp ist, können Sie einen Downcast mit a static_cast
ausführen. Boost bietet polymorphic_downcast
einen Downcast mit dynamic_cast
und assert
im Debug-Modus und im Release-Modus wird nur a verwendet. static_cast
)
Ich bin nicht der Meinung, dass Sie niemals den Typ eines Objekts in C ++ überprüfen möchten. Wenn Sie es vermeiden können, stimme ich zu, dass Sie sollten. Zu sagen, dass Sie dies NIEMALS tun sollten, geht jedoch zu weit. Sie können dies in sehr vielen Sprachen tun, und es kann Ihr Leben viel einfacher machen. Howard Pinsley zum Beispiel hat uns in seinem Beitrag auf C # gezeigt, wie es geht.
Ich arbeite viel mit dem Qt Framework. Im Allgemeinen modelliere ich meine Arbeit nach der Art und Weise, wie sie Dinge tun (zumindest wenn ich in ihrem Rahmen arbeite). Die QObject-Klasse ist die Basisklasse aller Qt-Objekte. Diese Klasse hat die Funktionen isWidgetType () und isWindowType () als schnelle Unterklassenprüfung. Warum also nicht in der Lage sein, Ihre eigenen abgeleiteten Klassen zu überprüfen, was seiner Natur nach vergleichbar ist? Hier ist ein QObject-Spin-off einiger 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 er auf Ihre abgeleitete Klasse verweist, indem Sie die statische Elementfunktion aufrufen:
if( MyQObject::isThisType( qObjPtr ) ) qDebug() << "This is a MyQObject!";
Ich weiß nicht, ob ich Ihr Problem richtig verstehe, also lassen Sie es mich in meinen eigenen Worten wiederholen ...
Problem: Geben Sie bei gegebenen Klassen B
und an D
, ob D
es sich um eine Unterklasse von handelt B
(oder umgekehrt?).
Lösung: Verwenden Sie etwas Vorlagenmagie! Okay, im Ernst, Sie müssen sich LOKI ansehen, eine hervorragende Meta-Programmierbibliothek für Vorlagen, die vom legendären C ++ - Autor Andrei Alexandrescu erstellt wurde.
Laden Sie LOKI herunter und fügen Sie den Header TypeManip.h
daraus in Ihren Quellcode ein. Verwenden Sie dann die SuperSubclass
Klassenvorlage wie folgt:
if(SuperSubClass<B,D>::value)
{
...
}
Laut Dokumentation SuperSubClass<B,D>::value
gilt, ob B
es sich um eine öffentliche Basis D
handelt oder ob B
und D
Aliase des gleichen Typs sind.
dh entweder D
ist eine Unterklasse von B
oder D
ist die gleiche wie B
.
Ich hoffe das hilft.
bearbeiten:
Bitte beachten Sie, dass die Auswertung von SuperSubClass<B,D>::value
zur Kompilierungszeit erfolgt, im Gegensatz zu einigen Methoden, die verwendet dynamic_cast
werden. Daher gibt es keine Strafe für die Verwendung dieses Systems zur Laufzeit.
#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
Sie können dies nur zur Kompilierungszeit mithilfe von Vorlagen tun, es sei denn, Sie verwenden RTTI.
Hiermit können Sie die Funktion typeid verwenden, die einen Zeiger auf eine type_info-Struktur liefert, die Informationen zum Typ enthält.
Lesen Sie es bei Wikipedia nach
In c # kann man einfach sagen:
if (myObj is Car) {
}
Ich dachte an die Verwendung von
typeid()
...
Nun ja, es könnte durch Vergleichen von : 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
}
}
std::is_base_of
nicht wie gewünscht funktioniert. : 3