C ++でオブジェクトのタイプを見つける


147

クラスAとそれを継承する別のクラスBがあります。タイプAのオブジェクトをパラメーターとして受け入れる関数をオーバーライドしているので、Aを受け入れる必要があります。ただし、後でBのみが持つ関数を呼び出すと、したがって、渡されたオブジェクトがタイプBでない場合は、falseを返し、続行しないことを望みます。

私の関数に渡されたオブジェクトがどのタイプであるかを知る最良の方法は何ですか?

回答:


162

dynamic_castはトリックを行う必要があります

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

dynamic_castキーワードは、キャストの妥当性を確保するために、ランタイムチェックを行う、別のポインタまたは参照型からデータムをキャストします。

実際のオブジェクトのタイプではないタイプへのポインターにキャストしようとすると、キャストの結果はNULLになります。実際のオブジェクトのタイプではないタイプへの参照にキャストしようとすると、キャストによってbad_cast例外がスローされます。

dynamic_castを機能させるには、Baseクラスに少なくとも1つの仮想関数があることを確認してください。

ウィキペディアのトピック実行時の型情報

RTTIは、ポリモーフィックなクラスでのみ使用できます。つまり、クラスには少なくとも1つの仮想メソッドがあります。実際には、これは制限ではありません。基本クラスには仮想デストラクタが必要であり、派生クラスのオブジェクトがベースポインタから削除された場合に適切なクリーンアップを実行できるようにする必要があるためです。


1
dynamic_castを機能させるには、Baseクラスに仮想関数が必要です。それは私には重要だと思われます。
GiCo、2015

3
OKが見つかりました:実行時の型情報(RTTI)は、ポリモーフィックなクラスでのみ使用できます。つまり、少なくとも1つの仮想メソッドがあることを意味します。dynamic_castとtypeidにはRTTIが必要です。
GiCo

dynamic_cast変換できない場合はスローしませんか?スローを生成せずにそれを行う方法はありますか?
jww

A* aptr = dynamic_cast<A*>(ptr);//これはこのようになるはずではありません
Mehdi Karamosly

これは、キャストされたPODで機能しますuint8_t*か?つまり、私はそれをチェックすることができuint32_t* x = dynamic_cast<uint32_t*>(p)、どこpuint8_t*?(私は罰則違反のテストを見つけようとしています)。
-jww

157

問題の説明には動的キャストが最適ですが、クラスタイプを次のように検索できることを追加しておきます。

#include <typeinfo>

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

4
オブジェクトが何であるか本当にわからない場合に適しています。受け入れられた答えはあなたがそうすることを前提としています。
unludo

4
@xusはい。stdヘッダーの一部
Amey Jah 2013

8
私は表示されませんか。タイプID名は有用である必要はなく、実装で定義されています。
2014年

3
ここで最も興味深いのは、同じクラスのインスタンスの名前が同じである必要がないことです。しかしタイプID自体は、同じクラスのインスタンスの同じ比較しなければならない参照stackoverflow.com/questions/1986418/typeid-versus-typeof-in-c
FourtyTwo

1
注gccは、結合された名前を返します11MyClass。分解するには、のABI拡張ライブラリを使用できますcxxabi.h。これはあなたを与えるabi::__cxa_demangleあなたの本当の名前与えるであろう
デヴィッド・G

27

これはRTTIと呼ばれますが、型を見つけてそれに基づいて特別なことを行うとコードがより脆弱になるため、ほぼ間違いなくここで設計を再検討する必要があります。


4
そうだね。私は本当にデザイン、またはクラスAには何も変えて行くことはできませんので、残念ながら、私は、既存のプロジェクトに取り組んでいます
lemnisca

11

完全にするために、私はRobocideからビルドをビルドし、typeidname()を使用せずに単独で使用できることを指摘します。

#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;
}

出力:

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

9

おそらくオブジェクトにID「タグ」を埋め込み、それを使用してクラスAのオブジェクトとクラスBのオブジェクトを区別します。

ただし、これは設計上の欠陥を示しています。理想的には、AにないBのメソッドはAの一部である必要がありますが、空のままにして、Bがそれらを上書きします。これにより、クラス固有のコードが不要になり、OOPの精神に沿ったものになります。



4

あなたのクラスはポリモーフィックではないからです。試してください:

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

BaseClasは多態的です。構造体のメンバーはデフォルトでパブリックであるため、クラスを構造体に変更しました。


3

あなたの説明は少し混乱しています。

一般的に言えば、一部のC ++実装にはそのためのメカニズムがありますが、型について尋ねる必要はありません。代わりに、Aへのポインターでdynamic_castを実行することになっています。これにより、実行時に、Aへのポインターの実際の内容がチェックされます。Bがある場合、Bへのポインタを取得します。それ以外の場合、例外またはnullを取得します。


1
失敗する参照キャストを実行した場合にのみ例外が発生することに注意してください。すなわち、dynamic_cast <T&>(t)。失敗したポインタキャストはNULLを返します。ie dynamic_cast <T *>(t)
AlfaZulu

いや、私はそれをより明確にすべきだった。ありがとう。値ではなく参照であるC型での記述という単語があったらいいのにと思います。
ウリ

3

他の人が示したように、dynamic_castを使用できます。ただし、一般に、取り組んでいる派生クラスのタイプを見つけるためにdynamic_castを使用することは、設計が悪いことを示しています。Aのポインターをパラメーターとして取る関数をオーバーライドする場合は、クラスA自体のメソッド/データを処理でき、クラスBのデータに依存しないようにする必要があります。作成しているメソッドがクラスBでのみ機能することを確認してから、クラスBで新しいメソッドを作成する必要があります。


1

オーバーロードされた関数を使用します。dynamic_castまたは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
    }
};

元の質問の右側:「後でBのみが持つ関数を呼び出す」-このような場合、オーバーロードはどのように機能しますか?
Marcin Gil、

AでBarを呼び出すと、Bは発生しません。BでBarを呼び出すと、Bにのみ存在するメソッドを呼び出すことができます。元の質問を読みますか?バーは彼の「タイプAのオブジェクトをパラメーターとして受け入れる関数をオーバーライドしています」
jmucchiello 2008

7
これは、質問者が使用していると思われる動的なポリモーフィズムでは機能しません。C ++は、コンパイル時の型にのみ基づいて、パラメーターのランタイムクラスに基づいてオーバーロードを選択できません。
スティーブジェソップ

1

ブーストライブラリにアクセスできる場合、おそらくtype_id_with_cvr()関数が必要です。const 、volatile、および&&修飾子を削除せずにデータ型を提供できます。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;
}

これがお役に立てば幸いです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.