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

Cha*_*had 61 c++ class subclass identification

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

Mar*_*ork 100

 

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @krlmlr.你能在编译时告诉`x`的类型吗?如果是这样,那么`static_cast <>()`就行了.如果在运行时之前无法告诉`x`的类型,那么你需要`dynamic_cast <>()` (8认同)

Dim*_*ima 42

你真的不应该.如果您的程序需要知道对象是什么类,那通常表明存在设计缺陷.看看您是否可以使用虚拟功能获得所需的行为.此外,有关您尝试做什么的更多信息将有所帮助.

我假设你有这样的情况:

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

void foo(Base *p)
{
  if(/* p is A */) /* do X */
  else /* do Y */
}
Run Code Online (Sandbox Code Playgroud)

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

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();
}
Run Code Online (Sandbox Code Playgroud)

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

  • 是的 - 这一切都很好 - 但这家伙想知道如何解决类型 (43认同)
  • +1.我认为这个名字的正确名称是"告诉,不要问".基本上,总是喜欢多态性(告诉对象做什么,让实现处理它)在case/if语句中你要求找出你正在处理的对象类型. (18认同)
  • 虽然正确,但这并不能回答这个问题. (12认同)
  • @Dima曾经使用过定义超类的外部库吗?请尝试在那里应用你的答案. (7认同)
  • 这个答案使得你可以控制你需要转换的类型并且可以重写它们的相当大的假设...例如我正在基于另一个GUI库向GUI库添加一个功能,我需要知道窗口小部件的父级是否可滚动.原始库无法测试这个,所以我必须尝试将我的小部件父级转换为可滚动小部件的基类,这真的很糟糕.无论如何,关键是你遗漏了手头问题的*实际*答案. (7认同)
  • @Dima,如果有人想知道语法仅仅是为了学习目的(假设他们正在阅读一本用Java编写的书,关于设计缺陷,他们需要将其转换为C++),该怎么办? (5认同)
  • 执行此操作(至少在Java中)的常见用例是比较两个对象是否相等.通常,您需要在检查所需的相等属性之前检查它们是否属于同一子类. (3认同)
  • 这只是您的个人意见,没有冒犯.相反,我认为在必要时将超类型转换为派生类没有任何问题.实际上,这是OOP使用的很大一部分 - 通过一些通用流程来管理类似的数据,但最终还是单独处理它们. (3认同)
  • @TomášZato,您当然可以找到您可能想知道对象的实际类型的情况.然而,这仍然意味着你正在与语言作斗争,并且存在潜在的设计缺陷.这个漏洞可能存在于第三方库中,但它仍然是一个缺陷.如果你无法控制它,那么你必须忍受它.如果你有控制权,那么你应该考虑修复底层设计. (2认同)
  • @TomášZato,这不仅仅是我个人的观点.如果您需要知道对象的派生类型,那么您的设计违反了Liskov替换原则:https://en.wikipedia.org/wiki/Liskov_substitution_principle这是Robert C. Martin的一篇论文,他在那里专门讨论了为什么要求因为对象的类型违反了LSP,为什么它是"面向对象设计的诅咒":http://www.objectment.com/resources/articles/lsp.pdf (2认同)
  • @Dima对于生产代码,我同意你的看法.但工厂的工作是根据其输入制作正确的子类.通过验证子类的属性是否与输入一致,可以一起测试工厂*和*每个子类*.但如果有一个错误,那么这样的测试将无法帮助任何人判断问题是在工厂还是在它构建的子类中.验证子类的类型是在这些测试中实现分离的一种方法. (2认同)
  • @Dima 投了反对票,因为它没有回答问题。并不是每个人都有使用设计完美的代码的自由。 (2认同)
  • 这是我犹豫是否要到 StackOverflow 解决编程问题的原因之一 - 这个问题不是问一个人是否**应该** 这样做 - 它问的是如何做。我在 Java 中一直使用 Java 中的 instanceof 运算符来执行此操作。在我现在使用 C++ 的特定情况下,我在 QT 中的自定义小部件的绘制例程中渲染对象,同时确保我可以添加一个 vfunc 来返回我想要渲染的项目的矩形,我不 b/c我已经有了黑白 6 和 12 vfunc,这在子类化时会带来痛苦,因为你必须拥有准确的拼写正确。无论哪种方式你都可以做到。 (2认同)
  • 这个答案不应该有任何争论:它根本没有回答问题。如果有人问“如何搬起石头砸自己的脚”,那么答案不应该是“请不要”。 (2认同)

Dre*_*all 22

你可以这样做dynamic_cast(至少对于多态类型).

实际上,在第二个想法 - 你不能告诉它是否特定类型与 - dynamic_cast但你可以告诉它是否是那种类型或其任何子类.

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

  • @OllieFord:当没有任何虚函数时. (4认同)
  • 啊哦 谢谢。 (3认同)

ajn*_*neu 7

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

  • 虚函数
  • 类型标识
  • 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;
}
Run Code Online (Sandbox Code Playgroud)

上面的程序打印如下:

type A
type A
type A
Run Code Online (Sandbox Code Playgroud)


cop*_*pro 6

dynamic_cast可以确定类型是否包含继承层次结构中任何位置的目标类型(是的,它是一个鲜为人知的特性,如果B继承自AC,它可以A*直接转换为a C*).typeid()可以确定对象的确切类型.但是,这两者都应该非常谨慎地使用.如前所述,您应始终避免动态类型识别,因为它表明存在设计缺陷.(另外,如果你知道对象是为确保目标的类型,你可以做一个垂头丧气了static_cast.加速提供了一个polymorphic_downcast会做一个沮丧的用dynamic_castassert在调试模式下,并在释放模式将只使用一个static_cast).


Aut*_*act 5

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

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

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.

More specifically, download LOKI and include header TypeManip.h from it in your source code then use the SuperSubclass class template as follows:

if(SuperSubClass<B,D>::value)
{
...
}
Run Code Online (Sandbox Code Playgroud)

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

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

我希望这有帮助。

编辑:

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