如何检查对象的类型是否是 C++ 中的特定子类?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/307765/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:30:46  来源:igfitidea点击:

How do I check if an object's type is a particular subclass in C++?

c++classsubclassidentification

提问by Chad

I was thinking along the lines of using typeid()but I don't know how to ask if that type is a subclass of another class (which, by the way, is abstract)

我正在考虑使用,typeid()但我不知道如何询问该类型是否是另一个类的子类(顺便说一下,它是抽象的)

采纳答案by Dima

You really shouldn't. If your program needs to know what class an object is, that usually indicates a design flaw. See if you can get the behavior you want using virtual functions. Also, more information about what you are trying to do would help.

你真的不应该。如果您的程序需要知道一个对象是什么类,这通常表明存在设计缺陷。看看你是否可以使用虚函数获得你想要的行为。此外,有关您正在尝试做什么的更多信息会有所帮助。

I am assuming you have a situation like this:

我假设你有这样的情况:

class Base;
class A : public Base {...};
class B : public Base {...};

void foo(Base *p)
{
  if(/* p is A */) /* do X */
  else /* do Y */
}

If this is what you have, then try to do something like this:

如果这是你所拥有的,那么尝试做这样的事情:

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:Since the debate about this answer still goes on after so many years, I thought I should throw in some references. If you have a pointer or reference to a base class, and your code needs to know the derived class of the object, then it violates Liskov substitution principle. Uncle Bobcalls this an "anathema to Object Oriented Design".

编辑:由于关于这个答案的争论在这么多年后仍在继续,我想我应该提供一些参考资料。如果你有一个指向基类的指针或引用,并且你的代码需要知道对象的派生类,那么它就违反了Liskov 替换原则鲍勃叔叔称之为“对面向对象设计的诅咒”。

回答by Martin York

 

 

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

回答by Drew Hall

You can do it with dynamic_cast(at least for polymorphic types).

您可以使用dynamic_cast(至少对于多态类型)。

Actually, on second thought--you can't tell if it is SPECIFICALLY a particular type with dynamic_cast--but you can tell if it is that type or any subclass thereof.

实际上,再想一想——您无法判断它是否是特定类型的特定类型——dynamic_cast但您可以判断它是否是该类型或其任何子类。

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

回答by coppro

dynamic_castcan determine if the type contains the target type anywhere in the inheritance hierarchy (yes, it's a little-known feature that if Binherits from Aand C, it can turn an A*directly into a C*). typeid()can determine the exact type of the object. However, these should both be used extremely sparingly. As has been mentioned already, you should always be avoiding dynamic type identification, because it indicates a design flaw. (also, if you know the object is for sure of the target type, you can do a downcast with a static_cast. Boost offers a polymorphic_downcastthat will do a downcast with dynamic_castand assertin debug mode, and in release mode it will just use a static_cast).

dynamic_cast可以确定该类型是否在继承层次结构中的任何位置包含目标类型(是的,这是一个鲜为人知的功能,如果BAand继承C,它可以将 anA*直接变成 a C*)。typeid()可以确定对象的确切类型。但是,这两者都应该非常谨慎地使用。正如已经提到的,您应该始终避免动态类型识别,因为它表明设计缺陷。(此外,如果您知道对象是确定的目标类型,则可以使用 a 进行向下转换。Booststatic_cast提供了 a polymorphic_downcast,它将在调试模式下使用dynamic_castassert进行向下转换,而在发布模式下它只会使用 a static_cast)。

回答by Autodidact

I don't know if I understand your problem correctly, so let me restate it in my own words...

我不知道我是否正确理解你的问题,所以让我用我自己的话重述一下......

Problem: Given classes Band D, determine if Dis a subclass of B(or vice-versa?)

问题:给定类BD,确定是否DB(或反之亦然?)

Solution: Use some template magic! Okay, seriously you need to take a look at LOKI, an excellent template meta-programming library produced by the fabled C++ author Andrei Alexandrescu.

解决方案:使用一些模板魔法!好吧,说真的,您需要看看 LOKI,这是一个出色的模板元编程库,由传说中的 C++ 作家 Andrei Alexandrescu 制作。

More specifically, download LOKIand include header TypeManip.hfrom it in your source code then use the SuperSubclassclass template as follows:

更具体地说,下载LOKITypeManip.h在您的源代码中包含来自它的标头,然后使用SuperSubclass类模板,如下所示:

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

According to documentation, SuperSubClass<B,D>::valuewill be true if Bis a public base of D, or if Band Dare aliases of the same type.

根据文档,SuperSubClass<B,D>::value如果B是 的公共基础D,或者如果BD是相同类型的别名,则为真。

i.e. either Dis a subclass of Bor Dis the same as B.

即要么D是 的子类,B要么D与 相同B

I hope this helps.

我希望这有帮助。

edit:

编辑:

Please note the evaluation of SuperSubClass<B,D>::valuehappens at compile time unlike some methods which use dynamic_cast, hence there is no penalty for using this system at runtime.

请注意,SuperSubClass<B,D>::value与某些使用 的方法不同,评估发生在编译时dynamic_cast,因此在运行时使用此系统不会受到惩罚。

回答by BuvinJ

I disagree that you should never want to check an object's type in C++. If you can avoid it, I agree that you should. Saying you should NEVER do this under any circumstance is going too far though. You can do this in a great many languages, and it can make your life a lot easier. Howard Pinsley, for instance, showed us how in his post on C#.

我不同意你永远不应该想在 C++ 中检查对象的类型。如果你能避免它,我同意你应该这样做。说你在任何情况下都不应该这样做是太过分了。您可以使用多种语言执行此操作,它可以让您的生活更轻松。例如,Howard Pinsley 在他关于 C# 的帖子中向我们展示了如何。

I do a lot of work with the Qt Framework. In general, I model what I do after the way they do things (at least when working in their framework). The QObject class is the base class of all Qt objects. That class has the functions isWidgetType() and isWindowType() as a quick subclass check. So why not be able to check your own derived classes, which is comparable in it's nature? Here is a QObject spin off of some of these other posts:

我用 Qt 框架做了很多工作。一般来说,我模仿他们做事的方式(至少在他们的框架中工作时)。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 ); }
};

And then when you are passing around a pointer to a QObject, you can check if it points to your derived class by calling the static member function:

然后当你传递一个指向 QObject 的指针时,你可以通过调用静态成员函数来检查它是否指向你的派生类:

if( MyQObject::isThisType( qObjPtr ) ) qDebug() << "This is a MyQObject!";

回答by ajneu

The code below demonstrates 3 different ways of doing it:

下面的代码演示了 3 种不同的方法:

  • virtual function
  • typeid
  • dynamic_cast
  • 虚函数
  • 类型标识
  • 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;
}

The program above prints this:

上面的程序打印如下:

type A
type A
type A

回答by Reinaldo Guedes

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

Result:

结果:

IS A D22
IS A D2
IS A Base

回答by Ziezi

I was thinking along the lines of using typeid()...

我正在考虑使用typeid()......

Well, yes, it could be done by comparing: typeid().name(). If we take the already described situation, where:

嗯,是的,可以通过比较来完成: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 */
}

A possible implementation of foo(Base *p)would be:

一个可能的实现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
    }
}

回答by user32141

You can only do it at compile time using templates, unless you use RTTI.

除非使用 RTTI,否则只能在编译时使用模板来执行此操作。

It lets you use the typeid function which will yield a pointer to a type_info structure which contains information about the type.

它允许您使用 typeid 函数,该函数将产生一个指向 type_info 结构的指针,该结构包含有关类型的信息。

Read up on it at Wikipedia

维基百科上阅读它