一旦提到typeid()函数,就不得不提RTTI了。
RTTI(Run-Time Type Information 运行时类型信息),指程序有能力识别 基类的指针或引用 在运行时实际指向的对象的类型信息。
C++中与RTTI相关的操作符有两个:
(1)typeid 操作符:返回指针或引用所指对象的实际类型。
(2)dynamic_cast 操作符:将基类类型的指针或引用安全地转换为派生类型的指针和引用。
注意:这两个操作符均 只为带有一个或多个虚函数的类返回动态类型信息----即在运行时执行RTTI操作符;对于其他类型则返回静态类型的信息---即在编译时计算RTTI操作符。
再强调一下,如果表达式的类型是类类型且至少包含有一个虚函数,则typeid操作符返回表达式的动态类型,需要在运行时计算;否则,typeid操作符返回表达式的静态类型,在编译时就可以计算。
因为对于编译器来说,如果类中没有声明虚函数,则表明你不会用到类的多态特性,也就无需为你额外提供一个你不需要的RTTI了。这也是C++的设计原则之一,即:不会让你为你不需要的东西有所付出。
typeid()
头文件:# include <typeinfo>语法--两种形式:typeid (type) 、typeid (expression)即任意表达式或类型名。
常见的用途:比较两个表达式的类型,或者将表达式的类型与特定类型相比较。
typeid返回类型:const type_info&;
type_info是一个定义在std中的类,具体定义如下:
class type_info {
public:
/** Destructor first. Being the first non-inline virtual function, this
* controls in which translation unit the vtable is emitted. The
* compiler makes use of that information to know where to emit
* the runtime-mandated type_info structures in the new-abi. */
virtual ~type_info();
/** Returns an @e implementation-defined byte string; this is not
* portable between compilers! */
const char* name() const
{ return __name[0] == '*' ? __name + 1 : __name; }
bool before(const type_info& __arg) const;
bool operator==(const type_info& __arg) const;
bool operator!=(const type_info& __arg) const
{ return !operator==(__arg); }
//......
protected:
const char *__name;
explicit type_info(const char *__n): __name(__n) { }
private:
/// Assigning type_info is not supported.
type_info& operator=(const type_info&);
type_info(const type_info&);
};
ISO C++标准并没有确切定义type_info,它的确切定义编译器相关的,但是标准却规定了其实现必需提供如下四种操作:
t1 == t2 | 如果两个对象t1和t2类型相同,则返回true;否则返回false |
t1 != t2 | 如果两个对象t1和t2类型不同,则返回true;否则返回false |
t.name() | 返回类型的C-style字符串,类型名字用系统相关的方法产生 |
t1.before(t2) | 返回指出t1是否出现在t2之前的bool值 |
type_info类没有默认构造函数,它的拷贝构造函数和赋值操作符都定义为private,同时提供protected的显示C风格字符串初始化构造函数,以及public的虚析构函数,这表明它期望用户能够使用它作为基类。
总之,我们可以使用typeid操作符来创建type_info对象。
dynamic_cast<>()
将基类类型的指针或引用安全地转换为派生类型的指针和引用。这句话清楚地表明了,dynamic_cast<>()只用于类的指针或引用在继承体系中的子父类转换。同时,名字中的dynamic表时基类中必须存在虚函数,否则编译器会认为你写的类不需要多态,故而对于dynamic_cast操作符,在编译的时候就会报错。
因为涉及到指针或引用的类运行多态,所以显然继承一定是public继承。当然,如果是在类的成员函数中使用多态,那可以放宽到protected继承。
如果可以安全地转换,则返回实际对象的地址,否则返回一个空指针。
RTTI与虚函数
如果不使用虚函数而只用RTTI的话:
Circle和Square皆是由Figure所衍生出来的子类别﹐它们各有自己的draw()函数。如果使用typeid(),则代码如下:
void drawing( Figure *p ) {
if(typeid(*p).name() == "Circle")
static_cast<Circle*>(p)->draw();
else if(typeid(*p).name() == "Rectangle")
static_cast<Rectangle*>(p)->draw();
}
当Figure类 再派生出新子类的时候,就还要修改上面的函数,再多加一个else if。这太坑了。
但是如果使用虚函数,将draw()声明为虚拟函数,则代码如下,一劳永逸。
void drawing(Figure *p) {
p->draw();
}
最后,如果发现在扩展的if else语句系列中使用了typeid(),则就要考虑是否应该使用虚函数和dynamic_cast了。