Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

 

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

Ответ 3

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

Собственно, во второй раз - вы не можете определить, является ли СПЕЦИАЛЬНО конкретным типом с dynamic_cast - но вы можете определить, является ли он этим типом или любым его подклассом.

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

Ответ 4

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

Ответ 5

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

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

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

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

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

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

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

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

изменить

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

Ответ 6

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

Я много работаю с 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!";

Ответ 7

#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

Ответ 8

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

if (myObj is Car) {

}

Ответ 9

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

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

Прочитайте его на Wikipedia

Ответ 10

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

Ответ 11

Код ниже демонстрирует 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