web-dev-qa-db-ja.com

オブジェクトの型がC ++の特定のサブクラスであるかどうかを確認するにはどうすればよいですか?

私はtypeid()を使用する線に沿って考えていましたが、その型が別のクラスのサブクラスであるかどうかを尋ねる方法がわかりません(これは抽象クラスです)

59
Chad

あなたは本当にすべきではありません。プログラムがオブジェクトがどのクラスであるかを知る必要がある場合、それは通常、設計上の欠陥を示しています。仮想関数を使用して、必要な動作を取得できるかどうかを確認してください。また、何をしようとしているかについての詳細情報が役立ちます。

私はあなたがこのような状況にあると仮定しています:

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();
}

編集:この答えについての議論は何年も経ってまだ続いているので、私はいくつかの参考文献を投げ込むべきだと思いました。基本クラスへのポインタまたは参照があり、コードがオブジェクトの派生クラスを知る必要がある場合、 リスコフ置換原理 に違反します。 ボブおじさん はこれを " オブジェクト指向設計への数学 "と呼びます。

41
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;
  }
}
92
Martin York

dynamic_castでそれを行うことができます(少なくとも多相型の場合)。

実際、考え直してみると、それがdynamic_castで特定の型であるかどうかはわかりませんが、その型またはそのサブクラスであるかどうかはわかります。

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

dynamic_castは、継承階層のどこかにタイプにターゲットタイプが含まれているかどうかを判断できます(はい、BAおよびCから継承する場合、A*C*に直接)。 typeid()は、オブジェクトの正確なタイプを判別できます。ただし、これらは両方とも非常に控えめに使用する必要があります。既に述べたように、動的な型識別は設計上の欠陥を示しているため、常に避けるべきです。 (また、オブジェクトが確実にターゲットタイプであることを知っている場合は、static_castを使用してダウンキャストを実行できます。Boostはpolymorphic_downcastを提供し、デバッグモードおよびリリースモードでdynamic_castおよびassertを使用してダウンキャストを実行しますstatic_castを使用するだけです。

6
coppro

私はあなたの問題を正しく理解しているかどうかわからないので、私自身の言葉でそれをもう一度言いましょう...

問題:クラスBおよびDが与えられた場合、DBのサブクラスであるかどうかを判断します(またはその逆ですか?)

解決策:テンプレートの魔法を使用してください!さて、真剣に、伝説のC++作家Andrei Alexandrescuによって作成された優れたテンプレートメタプログラミングライブラリLOKIを見てみる必要があります。

具体的には、 [〜#〜] loki [〜#〜] をダウンロードし、ソースコードにヘッダーTypeManip.hを含めてからSuperSubclassを使用します次のようなクラステンプレート:

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

文書によると、BDのパブリックベースである場合、またはBDがエイリアスの場合、SuperSubClass<B,D>::valueはtrueになります。同じタイプ。

つまり、DBのサブクラスであるか、DBと同じです。

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

編集:

SuperSubClass<B,D>::valueを使用する一部のメソッドとは異なり、コンパイル時にdynamic_castの評価が行われることに注意してください。したがって、実行時にこのシステムを使用してもペナルティはありません。

4
Autodidact

私は、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!";
4
BuvinJ
#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
2
Reinaldo Guedes

C#では、単に次のように言うことができます。

if (myObj is Car) {

}
1
Howard Pinsley

RTTIを使用しない限り、テンプレートを使用してコンパイル時にのみ実行できます。

Typeid関数を使用して、タイプに関する情報を含むtype_info構造体へのポインターを生成できます。

Wikipedia でそれを読んでください。

1
user32141

私は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
    }
}
_
0
Ziezi

以下のコードは、それを行う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
0
ajneu