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

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

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() можно определить точный тип объекта. Однако, они должны использоваться очень экономно. Как уже упоминалось, вы всегда должны избегать динамической идентификации типов, потому что это указывает на недостаток дизайна. (также, если вы знайте, что объект уверен в целевом типе, вы можете сделать downcast с static_cast. Boost предлагает polymorphic_downcast это сделает уныние с dynamic_cast и assert в режиме отладки и в режиме релиза он будет просто использовать static_cast).


Я не согласен с тем, что вы никогда не должны проверять тип объекта в C++. Если вы можете избежать этого, я согласен, что вы должны. Однако говорить, что вы никогда не должны этого делать ни при каких обстоятельствах, слишком далеко. Вы можете сделать это на многих языках, и это может сделать вашу жизнь намного проще. Говард Пинсли, например, показал нам, как в своем посте на 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!";

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

проблема: учитывая классы 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
    }
}