2008-11-21 5 views

ответ

37

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

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

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: Поскольку дискуссий об этом ответе все еще идет после стольких лет, я думал, что должен бросить некоторые ссылки. Если у вас есть указатель или ссылка на базовый класс, и ваш код должен знать производный класс объекта, то он нарушает Liskov substitution principle. Uncle Bob называет это «anathema to Object Oriented Design».

+13

+1. Я думаю, что правильное название для этого - «Скажи, не спрашивай». В принципе, всегда предпочитайте полиморфизм (TELLing object, что делать, позволяя реализации позаботиться об этом) над оператором case/if, в котором вы спрашиваете, какой тип объекта вы имеете в виду. – LeopardSkinPillBoxHat 2008-11-21 04:09:22

3

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

if (myObj is Car) { 

} 
+4

Я ответил на этот вопрос до плаката редактировать-эд свой вопрос и указал на его выбор языка. – 2008-11-21 20:05:24

+0

Я поддерживаю, это не ответ, что ОП указал его запрос. – 2015-09-15 14:43:32

+0

Ну, теперь он указал C++, поэтому этот ответ неверен – RecursiveExceptionException 2017-03-01 17:01:09

0

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

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

Читайте на него в Wikipedia

+0

Проголосовал за упоминание RTTI в этом контексте, который все остальные просто игнорировали. – ManuelSchneid3r 2016-07-19 20:57:16

76

 

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

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

На самом деле, на второй взгляд - вы не можете определить, является ли конкретным конкретным типом с dynamic_cast - но вы можете определить, является ли он этим типом или любым его подклассом.

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

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

4

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

Проблема: Учитывая классы 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, следовательно, нет никакого штрафа за использование этой системы во время выполнения.

4

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

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