8.4 合理使用RTTI
因为使用RTTI能从一个匿名基类的多态指针上发现类型信息。初学者很容易误用它,因为在学会使用虚函数进行多态调用方法之前,使用RTTI很有效。对于许多有过程化编程背景的人来说,不将程序组织成switch语句的集合是很困难的。借助RTTI他们可以实现这个愿望,但这样就损失了多态性在代码开发和维护过程中的重要价值。C++的目的就是希望用虚函数的多态机制贯穿代码的始终,只在必须的时候使用RTTI。
然而,使用虚函数多态机制的方法调用,要求我们拥有基类定义的控制权,因为在程序扩充的某些地方,可能会发现基类并没有包含我们所需要的虚函数。如果基类来自一个库或者由别人控制,这时RTTI就是一种解决该问题的方案;可以派生一个新类,并且添加我们需要的成员函数。在程序代码的其他地方,可以检查到我们这个特定的类,并且调用它的成员函数。这样做不会破坏多态性和程序的扩展能力,因为添加这样一个新类将不需要在程序中搜索switch语句。然而,当需要在程序主体中增加所需的新特征的代码时,则必须使用RTTI来检查该特定的类型。
如果只是为了某个特定类的利益而在基类中放进某种新特性,这意味着由那个基类派生出的所有其他子类都为一个纯虚函数而需要保留这些毫无意义的东西。这将使接口变的更不清晰,因为我们必须覆盖由基类继承来的所有纯虚函数,这是很令人烦恼的。
最后一点,RTTI有时可以解决效率问题。如果你的程序漂亮地运用了多态性,但是某个对象是以一种极低效的方式达到这个目的的,那么就将那个类挑出来,使用RTTI,并通过为其编写特别的代码来提高效率。
垃圾再生器
为了更进一步地举例说明RTTI的实际用途,下面的程序模拟了一个垃圾再生器。不同种类的“垃圾”被插入一个容器中,然后根据它们的动态类型进行分类。
用来表示垃圾类型单价的static值定义在实现文件中:
sumValue()模板从头到尾对一个容器进行迭代,显示并计算结果:
因为垃圾被不加分类地投入一个容器中,这样一来,垃圾的所有具体类型信息就“丢失”了。但是,为了稍后适当地对废料进行分类,具体类型信息必须恢复,这将用到RTTI。
可以通过使用map来改进这种解决方案,该map将指向type_info对象的指针与一个包含Trash指针的vector关联起来。因为映像需要一个能识别排序的判定函数,这里提供了一个名为TInfoLess的结构,它调用type_info:before()。当将Trash指针插入映像中的时候,这些指针将与type_info关键字自动关联。注意,这里必须对sumValue()进行不同的定义。
为了直接调用type_info:name(),我们在这里修改了sumValue(),因为作为TrashMap:value_type对的第1个成员,type_info对象现在是可获得的。这样就避免了为了获得正在处理的Trash的类型名而额外调用typeid,而这在该程序的以前版本中却是必须做的。