为什么使用typeid关键字设计不好?

xcr*_*ypt 10 c++ typeid

我听过很多人说任何使用typeid都是糟糕的设计,但对我来说似乎它提供了很好的实用性.

  1. 何时(和为什么)使用typeid"糟糕的设计"?
  2. 何时使用typeid可接受?
  3. 如果不可接受,但你仍然需要类似的东西,那么有什么选择可以设计好的?

Nic*_*las 18

问题不在于typeid.问题是看到typeid会鼓励你写这个:

PolymorphicType *pType = ...;
if(typeid(*pType) == typeid(Derived1))
  pType->Func1();
else if(typeid(*pType) == typeid(Derived2))
  pType->Func2();
else if(typeid(*pType) == typeid(Derived3))
  pType->Func3();
Run Code Online (Sandbox Code Playgroud)

这就是我们所说的"非常愚蠢".这是以尽可能最不合理的方式完成的虚函数调用.typeid在用于替换dynamic_castvirtual运行时有可能被滥用.

这个例子可能听起来很牵强.毕竟,很明显这只是一个虚拟电话.但是,糟糕的代码通常会从阻力最小的路径发展而来; 所需要的只是一个人做一个typeid() == typeid(),这个代码的种子已经开始了.一般来说,如果你typeid经常直接使用,你可能会做其他语言结构更好的事情.

typeid最后的多态类型推导方法.

所有用法都typeid错了吗?当然不是.boost::any没有它就不可能.那么它是可能的,但它不会比任何更安全void*.typeid是什么使类型安全boost::any型擦除成为可能.还有其他合法用途.

但是在代码行与使用比率方面,我建议它最多应该在10,000行代码中.比这少得多,你可能错了.

类型检查是否缓慢?

通常,调用的主要原因typeid是模板化代码(如在boost::any)中或者当您期望多态类型时.如果类型是静态确定的(即:给出了非多态类型的类型名称或值),那么您可以期望它在编译时完成.

这是你应该关注的多态值.我见过一个性能测试表明,部分typeid实现实际行走的类层次结构,因此它需要为他们找到类型的时间是成正比的实际类型和给定类型之间的类的数量.每个实现都会有所不同,但这是一个很好的指示,也许你不应该把它放在性能关键代码中.

  • `typeid(x).name()`对于编写调试/日志语句也很方便. (3认同)
  • @BenVoigt:"但是这个用法实际上看起来有些破碎,你不能放入Derived对象并转换为Base*." 破碎在旁观者的眼中.将`Derived*`强制转换为`void*`然后将其转换为`Base*`是不合法的.所以使用`boost :: any`这样做是不合法的.对我来说,这是`boost :: any`正在做的工作:你完全回来*只有*你投入的东西. (2认同)

rua*_*akh 6

我认为没有人说使用typeid本身就是糟糕的设计; 相反,你会听到是那种用法typeid表示糟糕的设计.这个想法存在着区分(比如说)的任何逻辑SquareCircle实际上应该是那些类,所以应该由一个虚函数(多态性)来表示.

不用说,这不是一个绝对的规则.

  • @xcrypt除非`typeid`的操作数是多态类型,`typeid`在编译时完成.如果操作数是多态类型,它的速度或速度取决于编译器编写器如何实现它.如果他们通过让程序将程序文本发送给公司员工并让他将类的类型输回到程序来实现它,那么它将会很慢. (3认同)

Ben*_*igt 6

可惜那些给你这个好建议的人却无法给出理由。这意味着他们是简单的货物崇拜程序员。

所有表达式都有静态编译时类型(模板扩展后)。所有类型要么是多态的,要么不是。

我将尝试为您详细说明为什么typeid(最接近)在这两种情况下都是无用的:

  1. typeid当表达式的静态类型不是多态时,没有理由使用它。编译器将使用静态类型,而不是动态类型,因此编译时函数重载有效。使用模板化类型特征(例如is_same.

  2. 如果表达式的静态类型是多态的,则typeid失败。是的,它将提供有关对象的动态类型的信息。但仅限于最派生的动态类型。这违反了里氏替换原则,因为给定类型Base和,添加派生自的Child新类型将不会被按原样处理。开闭原则也被违反,因为如果不重写所有内容,就无法使用新类型扩展设计。没有封装,因为有关类层次结构的信息必须分散在整个程序中,从而产生强耦合。 克服了其中一些但不是全部问题。GrandchildChildChilddynamic_cast

总之,一个使用的程序typeid将很难维护和测试。

而且,与任何规则一样,可能存在有限的例外。您使用 OO 语言的继承和虚函数功能,但实际上并不需要多态设计。我可以想到一个:您想要将类型名称记录为字符串,使用type_info::name(). 您也许还可以使用它来检测切片(其中已经违反了多态性)。但这仅适用于调试,而不适用于控制程序流程。

  • “void*”的语义*是“any”所需的语义。一般来说,您使用“any”,因为您无法通过特定位置传递完全类型化的对象。例如,通用消息和信号。在这种情况下,您只需接受离域耦合,因为好处(能够实际*工作*)超过缺点(获得any_cast异常的可能性)。生活在坚固的象牙塔和一切中真是太好了,但有时你必须做一些不太好的事情并接受增加的维护价格。最好有“any”,这样维护起来更容易 (3认同)
  • “当表达式的静态类型不是多态时,没有理由使用 typeid。” 除非您正在编写“boost::any”,并且您希望向用户保证他们推入变量的静态类型与他们试图从中转换的静态类型相同。你知道,99% 的要点都是“boost::any”。 (2认同)