C++11中与Java的instanceof等效的是什么?

8
我想知道现代C++11中与Java的instanceof等效的方法。我看过这个SO post,但它相当陈旧,想知道在C++11中是否有更现代、更好的解决方案?
我希望有可能使用一个switch结构来避免手动使用enum class。
class A {

};

class B : public A {

}

class C : public A {

}

on_event(A& obj)
{
    switch (obj) {
       case A:
       case B:
       case C:
    }
}

我的基类没有任何虚方法或函数。我代表一个解析器的表达式树,而基类只是一个多态容器 - 就像Haskell / OCaml中的ADT。


3
你是在问 dynamic_cast<> 吗?这并不是特定于 C++11 的语法。 - πάντα ῥεῖ
可能会感兴趣:http://stackoverflow.com/q/25495733/596781 - Kerrek SB
1
这个问题现在已经荒谬了:你正在寻求与Java完全无关且不可比较的东西的“Java等效物”。 - Kerrek SB
2
我不明白为什么这是“荒谬的”,你对我来说显得很粗鲁。我想知道实现on_event函数以处理不在基类(A)中具有虚拟方法/成员的不同子类的最佳方法。我知道如何在Java中做到这一点,但想知道C++中最好的方法。 - pezpezpez
6
“我的基类没有任何虚方法或函数”和“基类只是一个多态的容器”的说法似乎是相互矛盾的。由于A是一个“基类”,它应该有一个虚析构函数。不管怎样,这听起来像是一个XY问题。很可能你可以通过使用虚函数来完全避免使用switch语句。 - Captain Obvlious
显示剩余3条评论
5个回答

12

相同的答案仍然适用,并且在C ++中一直是这样:

if (C * p = dynamic_cast<C *>(&obj))
{
    // The type of obj is or is derived from C
}
else
{
    // obj is not a C
}

这个构造需要A是多态的,即具有虚成员函数。

还要注意,这种行为与比较 typeid(obj) == typeid(C) 不同,因为后者测试精确类型标识,而动态转换以及Java的instanceof只测试目标类型是否是最派生对象类型的基类。


1
如果你没有运行时类型识别(RTTI),那个东西就不会起作用,无论你考虑什么,它只是为了让那段代码正常工作所必需的。 - user2485710
4
在C++中没有这样的东西。你不能“不具备RTTI”并且仍然使用C++。 - Kerrek SB
1
@KerrekSB:“你不能“没有RTTI”并且仍然拥有C ++”,这也是生活中的一个好方法,太好了,以至于难以置信。 - user2485710
4
不要那样做,那会让问题变得荒谬。你想找Java的等效方法,但是Java中的“一切”都是多态的。如果需要解决特定于C ++的问题,请提出一个新问题。 - Kerrek SB
2
由于这个问题标记了C++11,你可以用auto替换C *;) - fredoverflow
显示剩余12条评论

0

C++中的普通数据(POD)没有运行时类型信息。所描述的所有类都占用恰好1个字节,并且在任何具有空基类优化的编译器中具有相同的运行时表示。

因此,您想要的是无法实现的。

向基类添加虚析构函数会添加RTTI和dynamic_cast支持。

向基类添加一个在每个派生类中以不同方式初始化的enumint字段也可以起到作用。

另一种选择是创建一个模板函数,并存储指向它的指针,如下所示:

using my_type_id=void(*)();
template<class>void get_my_type_id_helper(){};
template<class T> my_type_id get_my_type_id(){return get_my_type_id_helper<T>;}

然后将适当初始化的my_type_id存储在A中。这是重新发明RTTI,随着您需要更多功能,您会接近C++ RTTI开销。

在C ++中,您只支付您要求的:您可以请求没有RTTI的类,并获得它。

RTTI是运行时类型信息。POD是C ++ 03术语,指纯粹的旧数据。许多类不是POD:最简单的方法是添加虚拟析构函数。C ++ 11具有更精细的标准布局和聚合术语。

从技术上讲,RTTI和POD并不是彼此的对立物:有些没有RTTI的类不是POD。

请注意,MSVC有选项可以不生成RTTI,并且其激进的Comdat折叠可能会破坏我上面做的手动RTTI,在两种情况下都违反了标准。


0

也许你会对我在你提到的旧SO帖子中发布的答案感兴趣。

https://dev59.com/aHRB5IYBdhLWcg3wz6ed#49296405

答案提供了一个基于C++11、模板元编程和RTTI的instanceof实现,而不使用dynamic_cast。一个小型性能测量应用程序证明,如果使用编译器优化,它比dynamic_cast更高效。

-1

不要这样做。在大多数情况下,当你使用instanceofdynamic_cast时,你应该重新审查你的设计。

为什么?你很可能违反了Liskov替换原则

那么这种方法怎么样:

class A {
   public:
     virtual void action();
     virtual ~A();
};

class B : public A {
   public: void action() override;
};

class C : public A {
   public: void action() override;
};

void on_event(A& obj)
{
   obj.action();
}

请注意,正如@ Yakk指出的那样,您需要至少一个virtual方法才能获得动态多态性。 有一条规则是:当您拥有至少一个虚拟方法时,始终在基类中编写虚拟析构函数。
您可以使用模板和专业化或类型标记来完成所有这些操作,但我从您的问题中了解到-来自Java-您还不想去那里。 您真的很喜欢虚拟方法,对吧? 很抱歉,在C ++中必须将它们标记出来。

我认为一个由3个类(A,B,C)组成的程序不违反LSP原则,可以被视为良好的设计。我本想投反对票,因为LSP部分是误导性的(问题是关于C++11的instanceof的直接替代方法,而你的警告已经在链接中提到了),但有人可能会发现它有用。 - m039

-1

如果你愿意限制自己只使用编译时已知的类型(而不是通过具有虚表的类实例上的指针进行操作),那么C++11及更高版本确实有一个instanceof等效函数:它就是std::is_base_of

你可能还想查看std::is_convertiblestd::is_same


3
除非他将其变成函数模板并在编译时知道类型,否则我不确定这如何帮助他。我认为很明显他在谈论运行时多态性。 - PeterT
@PeterT:完全不同意。在C++中,特别是现代C++中,我们可以使用编译时已知的类型来完成很多工作,因此这是一个完全有效的答案(即使它应该被编辑)。 - einpoklum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接