web-dev-qa-db-fra.com

Comment vérifier si le type d'un objet est une sous-classe particulière en C++?

Je pensais utiliser typeid() mais je ne sais pas comment demander si ce type est une sous-classe d'une autre classe (ce qui est d'ailleurs abstrait)

53
Chad

Tu ne devrais vraiment pas. Si votre programme a besoin de savoir quelle classe est un objet, cela indique généralement un défaut de conception. Voyez si vous pouvez obtenir le comportement que vous souhaitez en utilisant des fonctions virtuelles. En outre, il serait utile d’avoir plus d’informations sur ce que vous essayez de faire.

Je suppose que vous avez une situation comme celle-ci:

class Base;
class A : public Base {...};
class B : public Base {...};

void foo(Base *p)
{
  if(/* p is A */) /* do X */
  else /* do Y */
}

Si c'est ce que vous avez, essayez de faire quelque chose comme ceci:

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

Edit: Etant donné que le débat sur cette réponse continue après des années, je me suis dit que je devrais ajouter quelques références. Si vous avez un pointeur ou une référence à une classe de base et que votre code doit connaître la classe dérivée de l'objet, il enfreint le principe de substitution de Liskov . Oncle Bob appelle cela un " anathème à la conception orientée objet ".

36
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;
  }
}
86
Martin York

Vous pouvez le faire avec dynamic_cast (au moins pour les types polymorphes).

En fait, à bien y penser - vous ne pouvez pas savoir s'il s'agit spécifiquement d'un type particulier avec dynamic_cast-- mais vous pouvez savoir s'il s'agit de ce type ou de l'une de ses sous-classes.

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

dynamic_cast peut déterminer si le type contient le type cible n'importe où dans la hiérarchie d'héritage (oui, il s'agit d'une fonctionnalité peu connue qui, si B hérite de A et C, elle peut transformer un A* directement en un C*). typeid() peut déterminer le type exact de l'objet. Cependant, ils doivent tous deux être utilisés avec beaucoup de parcimonie. Comme cela a déjà été mentionné, vous devriez toujours éviter l'identification de type dynamique, car elle indique un défaut de conception. (De plus, si vous savez que l'objet est sûr du type de cible, vous pouvez effectuer un downcast avec un static_cast. Boost propose un polymorphic_downcast qui effectuera un downcast avec dynamic_cast et assert en mode débogage et en mode de publication, il utilisera simplement un static_cast).

6
coppro

Je ne sais pas si je comprends votre problème correctement, alors laissez-moi le reformuler avec mes propres mots ...

Problème: Étant donné les classes B et D, déterminez si D est une sous-classe de B (ou vice-versa?)

Solution: Utilisez des modèles de magie! Sérieusement, vous devez jeter un coup d’œil à LOKI, une excellente bibliothèque de méta-programmation pour modèles produite par le célèbre auteur C++, Andrei Alexandrescu.

Plus précisément, téléchargez LOKI et incluez l'en-tête TypeManip.h dans votre code source, puis utilisez le modèle de classe SuperSubclass comme suit:

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

Selon la documentation, SuperSubClass<B,D>::value sera vrai si B est une base publique de D ou si B et D sont des alias du même type.

c'est-à-dire que D est une sous-classe de B ou D est identique à B.

J'espère que ça aide.

modifier:

Veuillez noter que l'évaluation de SuperSubClass<B,D>::value se produit à la compilation contrairement à certaines méthodes qui utilisent dynamic_cast, il n'y a donc aucune pénalité pour l'utilisation de ce système au moment de l'exécution.

4
Autodidact

Je ne suis pas d'accord pour dire que vous ne devriez jamais vouloir vérifier le type d'un objet en C++. Si vous pouvez l'éviter, je conviens que vous devriez le faire. Dire que vous ne devriez JAMAIS faire cela en toutes circonstances va cependant trop loin. Vous pouvez le faire dans de très nombreuses langues et vous faciliter la vie beaucoup plus facilement. Howard Pinsley, par exemple, nous a montré comment dans son post sur C #. 

Je travaille beaucoup avec Qt Framework. En général, je modélise ce que je fais après la façon dont ils font les choses (du moins lorsque je travaille dans leur cadre). La classe QObject est la classe de base de tous les objets Qt. Cette classe a les fonctions isWidgetType () et isWindowType () en tant que vérification rapide de sous-classe. Alors pourquoi ne pas pouvoir vérifier vos propres classes dérivées, ce qui est comparable dans sa nature? Voici un extrait de QObject de certains de ces autres articles:

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

Et ensuite, lorsque vous passez un pointeur sur un QObject, vous pouvez vérifier s'il pointe vers votre classe dérivée en appelant la fonction membre static:

if( MyQObject::isThisType( qObjPtr ) ) qDebug() << "This is a MyQObject!";
4
BuvinJ

En c #, vous pouvez simplement dire:

if (myObj is Car) {

}
3
Howard Pinsley
#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;
  }
}

Résultat:

IS A D22
IS A D2
IS A Base
2
Reinaldo Guedes

Vous ne pouvez le faire qu'au moment de la compilation à l'aide de modèles, sauf si vous utilisez RTTI. 

Il vous permet d'utiliser la fonction typeid qui donnera un pointeur sur une structure type_info contenant des informations sur le type.

Lisez à ce sujet sur Wikipedia

1
user32141

Je pensais utiliser typeid()...

Eh bien, oui, cela pourrait être fait en comparant: typeid().name(). Si nous prenons la situation déjà décrite, où:

class Base;
class A : public Base {...};
class B : public Base {...};

void foo(Base *p)
{
  if(/* p is A */) /* do X */
  else /* do Y */
}

Une implémentation possible de foo(Base *p) serait:

#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

Le code ci-dessous illustre 3 manières différentes de le faire:

  • fonction virtuelle
  • typeid
  • diffusion_dynamique
#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;
}

Le programme ci-dessus affiche ceci:

type A
type A
type A
0
ajneu