私は使用の線に沿って考えていましたtypeid()
が、その型が別のクラスのサブクラスであるかどうかを尋ねる方法がわかりません(ちなみに、これは抽象的です)
14 に答える
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;
}
}
あなたは本当にすべきではありません。プログラムがオブジェクトのクラスを知る必要がある場合、それは通常、設計上の欠陥を示しています。仮想関数を使用して、必要な動作が得られるかどうかを確認してください。また、何をしようとしているのかについての詳細情報も役立ちます。
次のような状況があるとします。
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();
}
編集:この答えについての議論は何年も経った今でも続いているので、いくつかの参考文献を入れるべきだと思いました. 基本クラスへのポインターまたは参照があり、コードがオブジェクトの派生クラスを知る必要がある場合、それはLiskov 置換原則に違反します。ボブおじさんはこれを「オブジェクト指向設計へのアナテマ」と呼んでいます。
あなたはそれを行うことができますdynamic_cast
(少なくともポリモーフィック型の場合)。
実際、よく考えてみると、特定の型であるdynamic_cast
かどうかはわかりませんが、その型またはそのサブクラスであるかどうかはわかります。
template <class DstType, class SrcType>
bool IsType(const SrcType* src)
{
return dynamic_cast<const DstType*>(src) != nullptr;
}
以下のコードは、それを行う 3 つの異なる方法を示しています。
- 仮想機能
- typeid
- 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;
}
上記のプログラムはこれを出力します:
type A
type A
type A
dynamic_cast
型が継承階層のどこかにターゲット型を含むかどうかを判断できます (はい、andB
から継承する場合、を に直接変換できるというあまり知られていない機能です)。オブジェクトの正確なタイプを判別できます。ただし、これらは両方とも非常に慎重に使用する必要があります。すでに述べたように、動的な型識別は常に避けるべきです。これは設計上の欠陥を示しているからです。(また、オブジェクトが確実にターゲット タイプであることがわかっている場合は、 を使用してダウンキャストを実行できます。Boost は、とを使用してデバッグ モードでダウンキャストを実行し、リリース モードでは を使用するだけです)。A
C
A*
C*
typeid()
static_cast
polymorphic_downcast
dynamic_cast
assert
static_cast
あなたの問題を正しく理解しているかどうかわからないので、自分の言葉で言い直させてください...
B
問題: クラスおよびが与えられた場合、が のサブクラスであるD
かどうかを判断します(またはその逆?)D
B
解決策: テンプレート マジックを使用してください。LOKI は、有名な C++ の作者である Andrei Alexandrescu によって作成された優れたテンプレート メタプログラミング ライブラリです。
より具体的には、LOKIをダウンロードし、そこからヘッダーTypeManip.h
をソース コードにインクルードしてから、SuperSubclass
次のようにクラス テンプレートを使用します。
if(SuperSubClass<B,D>::value)
{
...
}
ドキュメントによると、が のパブリック ベースである場合、またはとが同じタイプのエイリアスであるSuperSubClass<B,D>::value
場合は true になります。B
D
B
D
つまりD
、 のサブクラスであるB
かD
、 と同じB
です。
これが役立つことを願っています。
編集:
SuperSubClass<B,D>::value
を使用する一部のメソッドとは異なり、 の評価はコンパイル時に行われることに注意してくださいdynamic_cast
。したがって、実行時にこのシステムを使用してもペナルティはありません。
C++ でオブジェクトの型をチェックしたくないという意見には同意しません。避けることができるなら、避けるべきだと私は同意します。ただし、いかなる状況下でもこれを行うべきではないと言うのは行き過ぎです。これは非常に多くの言語で行うことができ、生活がずっと楽になります。たとえば、Howard Pinsley は、C# に関する投稿でその方法を示しました。
私は Qt フレームワークで多くの作業を行っています。一般的に、私は彼らが物事を行う方法に従って自分の行動をモデル化します (少なくとも彼らのフレームワークで作業している場合)。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!";
私は使用の線に沿って考えていました
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
}
}
RTTI を使用しない限り、テンプレートを使用してコンパイル時にのみ実行できます。
型に関する情報を含む type_info 構造体へのポインターを生成する typeid 関数を使用できます。
ウィキペディアでそれを読んでください
C# では、次のように簡単に言うことができます。
if (myObj is Car) {
}