Как проверить, является ли тип объекта определенным подклассом в C++?


Я думал по линии использования typeid() но я не знаю, как спросить, является ли этот тип подклассом другого класса (который, кстати, является абстрактным)

10 51

10 ответов:

Я предполагаю, что у вас есть такая ситуация:

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

Edit: С момента дебатов об этом ответе все еще продолжается после стольких лет, я подумал, что должен добавить некоторые ссылки. Если у вас есть указатель или ссылка на базовый класс, и ваш код должен знать производного класса объекта, то он нарушает принцип замещения Лисков. Дядя Боб называет это "анафема объектно-ориентированному дизайну".

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 предлагает a polymorphic_downcast что будет делать потупив с dynamic_cast и assert в режиме отладки и в режиме релиза он будет просто использовать static_cast).

Я не согласен, что вы никогда не должны хотеть, чтобы проверить тип объекта в C++. Если вы можете избежать этого, я согласен, что вы должны. Однако говорить, что вы никогда не должны этого делать ни при каких обстоятельствах, слишком далеко. Вы можете сделать это на очень многих языках, и это может сделать вашу жизнь намного проще. Говард Пинсли, например, показал нам, как в своем посте на C#.

Я много работаю с фреймворком Qt. В общем, я моделирую то, что я делаю после того, как они делают вещи (по крайней мере, когда работая в их рамках). Класс QObject является базовым классом всех объектов Qt. Этот класс имеет функции isWidgetType() и isWindowType () в качестве быстрой проверки подкласса. Так почему бы не иметь возможность проверить свои собственные производные классы, которые сопоставимы по своей природе? Вот QObject spin off некоторых из этих других сообщений:

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!";

я не знаю, правильно ли я понимаю вашу проблему, поэтому позвольте мне повторить ее своими словами...

проблема: данные классы B и D, определить D является наследником B (или наоборот?)

решение: используйте некоторые шаблоны магии! Хорошо, серьезно вам нужно взглянуть на LOKI, отличную библиотеку метапрограммирования шаблонов, созданную легендарным автором C++ Андреем Александреску.

более конкретно, скачать Локи и включить заголовок TypeManip.h из него в исходном коде затем используйте SuperSubclass шаблон класса следующим образом:

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

согласно документации,SuperSubClass<B,D>::value будет true, если B открытая база D, или B и D являются псевдонимами одного типа.

либо D является наследником B или D это то же самое, что B.

я надеюсь, что это помогает.

edit:

Пожалуйста, обратите внимание на оценку SuperSubClass<B,D>::value происходит во время компиляции, в отличие от некоторых методов, которые используют dynamic_cast, следовательно, нет никакого наказания за использование этой системы во время выполнения.

В C# вы можете просто сказать:

if (myObj is Car) {

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

вы можете сделать это только во время компиляции с помощью шаблонов, Если вы не используете RTTI.

Это позволяет использовать функцию typeid, которая будет давать указатель на структуру type_info, которая содержит информацию о типе.

читайте на нем в Википедия

Я думал в направлении использования 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
    }
}