我听过很多人说typeid
的任何使用都是糟糕的设计,但对我而言似乎它提供了很好的实用性。
typeid
“糟糕的设计”?typeid
可以接受?答案 0 :(得分: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();
这就是我们所说的“非常愚蠢”。这是以尽可能最不合理的方式完成的虚函数调用。用于替换typeid
和dynamic_cast
函数时,virtual
可能会被滥用。
这个例子可能听起来很牵强。毕竟,很明显这只是一个虚拟电话。但是,糟糕的代码通常会从阻力最小的路径发展而来;所需要的只是一个人做typeid() == typeid()
,这个代码的种子已经开始了。一般来说,如果你经常直接使用typeid
,那么你正在采取其他语言结构可以做得更好的事情是非常好的。
typeid
是 last 度假胜地的多态类型演绎方法。
typeid
的所有使用是否都错了?当然不是。没有它,boost::any
是不可能的。好吧,它可能可能,但它不会比void*
更安全。 typeid
是使类型安全boost::any
类型擦除成为可能的原因。还有其他合法用途。
但是在代码行与使用比率方面,我建议它最多应该在10,000行代码中。比这少得多,你可能错了。
类型检查是否缓慢?
一般来说,调用typeid
的主要原因是模板化代码(如boost::any
)或者您期望多态类型。如果类型是静态确定的(即:给出了非多态类型的类型名称或值),那么您可以期望它在编译时完成。
这是你应该关注的多态值。我已经看到一个性能测试,它表明一些typeid
实现实际上走了类层次结构,因此它们找到类型所花费的时间与实际类型和给定类型之间的类数成正比。每个实现都会有所不同,但这是一个很好的指示,也许你不应该把它放在性能关键的代码中。
答案 1 :(得分:6)
我认为没有人说使用typeid
本身就是糟糕的设计;相反,你会听到的是typeid
的使用指示糟糕的设计。这个想法是,任何区分(比如说)Square
和Circle
的逻辑实际上应该是 in 那些类,所以应该用虚函数(多态)表示。 / p>
毋庸置疑,这不是一个绝对的规则。
答案 2 :(得分:4)
那些给你这个好建议的人无法说出理由。这意味着他们是简单的cargo cult程序员。
所有表达式都具有静态编译时类型(模板扩展后)。所有类型都是多态的或不是。
我会试着详细说明为什么typeid
(最接近的)在两种情况下都无用:
当表达式的静态类型不是多态时,没有理由使用typeid
。编译器将使用静态类型,而不是动态类型,因此编译时函数重载起作用。使用模板化类型特征(例如is_same
)可以更有效地检查类型标识。
如果表达式的静态类型是多态的,则typeid
会失败。是的,它将提供有关对象动态类型的信息。但只有最衍生的动态类型。这违反了Liskov替换原则,因为给定类型Base
和Child
,添加从Grandchild
派生的新类型Child
将不会被视为Child
。开放 - 封闭原则也被违反,因为在不重写所有内容的情况下,新设计无法扩展设计。没有封装,因为有关类层次结构的信息必须分散在整个程序中,从而产生强耦合。 dynamic_cast
克服了一些但并非所有这些问题。
简而言之,使用typeid
的程序将很难维护和测试。
而且,与任何规则一样,可能存在有限的例外情况。您正在使用继承和虚函数的OO语言功能,但实际上并不需要多态设计。我可以想到一个:您希望使用type_info::name()
将类型的名称记录为字符串。您也可以使用它来检测切片(已经违反了多态性)。但这仅适用于调试,绝不适用于控制程序流程。