私はtypeid()
を使用する線に沿って考えていましたが、その型が別のクラスのサブクラスであるかどうかを尋ねる方法がわかりません(これは抽象クラスです)
あなたは本当にすべきではありません。プログラムがオブジェクトがどのクラスであるかを知る必要がある場合、それは通常、設計上の欠陥を示しています。仮想関数を使用して、必要な動作を取得できるかどうかを確認してください。また、何をしようとしているかについての詳細情報が役立ちます。
私はあなたがこのような状況にあると仮定しています:
class Base;
class A : public Base {...};
class B : public Base {...};
void foo(Base *p)
{
if(/* p is A */) /* do X */
else /* do Y */
}
これがあなたの持っているものなら、このようなことをしてみてください:
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();
}
編集:この答えについての議論は何年も経ってまだ続いているので、私はいくつかの参考文献を投げ込むべきだと思いました。基本クラスへのポインタまたは参照があり、コードがオブジェクトの派生クラスを知る必要がある場合、 リスコフ置換原理 に違反します。 ボブおじさん はこれを " オブジェクト指向設計への数学 "と呼びます。
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
でそれを行うことができます(少なくとも多相型の場合)。
実際、考え直してみると、それがdynamic_cast
で特定の型であるかどうかはわかりませんが、その型またはそのサブクラスであるかどうかはわかります。
template <class DstType, class SrcType>
bool IsType(const SrcType* src)
{
return dynamic_cast<const DstType*>(src) != nullptr;
}
dynamic_cast
は、継承階層のどこかにタイプにターゲットタイプが含まれているかどうかを判断できます(はい、B
がA
およびC
から継承する場合、A*
C*
に直接)。 typeid()
は、オブジェクトの正確なタイプを判別できます。ただし、これらは両方とも非常に控えめに使用する必要があります。既に述べたように、動的な型識別は設計上の欠陥を示しているため、常に避けるべきです。 (また、オブジェクトが確実にターゲットタイプであることを知っている場合は、static_cast
を使用してダウンキャストを実行できます。Boostはpolymorphic_downcast
を提供し、デバッグモードおよびリリースモードでdynamic_cast
およびassert
を使用してダウンキャストを実行しますstatic_cast
を使用するだけです。
私はあなたの問題を正しく理解しているかどうかわからないので、私自身の言葉でそれをもう一度言いましょう...
問題:クラスB
およびD
が与えられた場合、D
がB
のサブクラスであるかどうかを判断します(またはその逆ですか?)
解決策:テンプレートの魔法を使用してください!さて、真剣に、伝説のC++作家Andrei Alexandrescuによって作成された優れたテンプレートメタプログラミングライブラリLOKIを見てみる必要があります。
具体的には、 [〜#〜] loki [〜#〜] をダウンロードし、ソースコードにヘッダーTypeManip.h
を含めてからSuperSubclass
を使用します次のようなクラステンプレート:
if(SuperSubClass<B,D>::value)
{
...
}
文書によると、B
がD
のパブリックベースである場合、またはB
とD
がエイリアスの場合、SuperSubClass<B,D>::value
はtrueになります。同じタイプ。
つまり、D
はB
のサブクラスであるか、D
はB
と同じです。
これがお役に立てば幸いです。
編集:
SuperSubClass<B,D>::value
を使用する一部のメソッドとは異なり、コンパイル時にdynamic_cast
の評価が行われることに注意してください。したがって、実行時にこのシステムを使用してもペナルティはありません。
私は、C++でオブジェクトの型をチェックしたくないことには同意しません。あなたがそれを避けることができるなら、私はあなたがそうすべきであることに同意します。しかし、どんな状況でもこれを絶対にしないでくださいと言うのは行き過ぎです。非常に多くの言語でこれを行うことができ、あなたの人生をずっと楽にすることができます。たとえば、Howard Pinsleyは、C#に関する彼の投稿でその方法を示しました。
私はQt Frameworkで多くの仕事をしています。一般的に、私は彼らが物事を行う方法の後に私が行うことをモデル化します(少なくともそのフレームワークで作業する場合)。 QObjectクラスは、すべてのQtオブジェクトの基本クラスです。そのクラスには、サブクラスのクイックチェックとしてisWidgetType()およびisWindowType()関数があります。それでは、独自の派生クラスを確認できないのはなぜですか?以下は、これらの他の投稿の一部から派生したQObjectです。
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 ); }
};
そして、QObjectへのポインターを渡すとき、静的メンバー関数を呼び出すことで、派生クラスを指すかどうかを確認できます。
if( MyQObject::isThisType( qObjPtr ) ) qDebug() << "This is a MyQObject!";
#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;
}
}
結果:
IS A D22
IS A D2
IS A Base
C#では、単に次のように言うことができます。
if (myObj is Car) {
}
RTTIを使用しない限り、テンプレートを使用してコンパイル時にのみ実行できます。
Typeid関数を使用して、タイプに関する情報を含むtype_info構造体へのポインターを生成できます。
Wikipedia でそれを読んでください。
私は
typeid()
...を使用する線に沿って考えていました...
ええ、そうです、それはtypeid().name()
と比較することでできます。すでに説明した状況をとる場合、ここで:
_class Base;
class A : public Base {...};
class B : public Base {...};
void foo(Base *p)
{
if(/* p is A */) /* do X */
else /* do Y */
}
_
foo(Base *p)
の可能な実装は次のとおりです。
_#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
}
}
_
以下のコードは、それを行う3つの異なる方法を示しています。
#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;
}
上記のプログラムはこれを印刷します:
type A
type A
type A