Как проверить, является ли тип объекта определенным подклассом в C ++? - PullRequest
57 голосов
/ 21 ноября 2008

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

Ответы [ 10 ]

91 голосов
/ 21 ноября 2008

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;
  }
}
40 голосов
/ 21 ноября 2008

Ты действительно не должен. Если вашей программе необходимо знать, к какому классу относится объект, это обычно указывает на недостаток дизайна. Посмотрите, сможете ли вы получить желаемое поведение с помощью виртуальных функций. Также может помочь дополнительная информация о том, что вы пытаетесь сделать.

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

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

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

21 голосов
/ 21 ноября 2008

Вы можете сделать это с помощью dynamic_cast (по крайней мере, для полиморфных типов).

На самом деле, если подумать - вы не можете сказать, является ли он ОСОБЕННО конкретным типом с dynamic_cast, - но вы можете сказать, является ли он этим типом или любым подклассом.

template <class DstType, class SrcType>
bool IsType(const SrcType* src)
{
  return dynamic_cast<const DstType*>(src) != nullptr;
}
6 голосов
/ 21 ноября 2008

dynamic_cast может определить, содержит ли тип целевой тип где-либо в иерархии наследования (да, это малоизвестная особенность, которая, если B наследует от A и C, может превратить A* прямо в C*). typeid() может определить точный тип объекта. Тем не менее, они оба должны использоваться крайне экономно. Как уже упоминалось, вы всегда должны избегать динамической идентификации типов, потому что это указывает на недостаток дизайна. (также, если вы знаете, что объект точно относится к целевому типу, вы можете выполнить понижение с помощью static_cast. Boost предлагает polymorphic_downcast, который будет выполнять понижение с dynamic_cast и assert в режиме отладки, и в режиме релиза он просто будет использовать static_cast).

4 голосов
/ 20 декабря 2015

Я не согласен с тем, что вы никогда не должны проверять тип объекта в 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!";
4 голосов
/ 21 ноября 2008

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

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

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

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

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

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

т.е. либо D является подклассом B, либо D совпадает с B.

Надеюсь, это поможет.

редактирование:

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

2 голосов
/ 26 мая 2017
#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
1 голос
/ 21 ноября 2008

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

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

Читайте об этом в Википедия

1 голос
/ 21 ноября 2008

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

if (myObj is Car) {

}
0 голосов
/ 07 июня 2017

Я думал о том, чтобы использовать 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
    }
}
...