c++ typeid操作符

前言

在分析typeid之前, 先了解什么是RTTI(运行时类型识别). RTTI使程序能够获取由基指针(引用)所指向的对象的实际派生类型, 允许用指向基类的指针(引用)来操作对象并能够获取到这些指针(引用)所指对象的实际派生类型. c++具体支持typeiddynamic_cast两个操作符.

typeid操作符用于返回对象的引用, 实际调用的是type_info标准库类型, 也在typeinfo头文件中. 并且typeid有两种形式, 一种在编译期获得类型, 一种在运行时通过RTTI获取类型.

注意 : typeid的类中的默认构造函数和复制构造函数都是被删除的, 所以typeid对象不能执行复制操作, 只能单独创建对象.

typeid支持的操作

运算 描述
t1 == t2 如果两个对象t1和t2类型相同,则返回true;否则返回false
t1 != t2 如果两个对象t1和t2类型不同,则返回true;否则返回false
t.name() 返回类型的C-style字符串,类型名字由编译器相关的方法产生
t1.before(t2) 返回指出t1是否出现在t2之前的bool值

typeid.name()产生的对象名是由编译器决定的, 所以不同的编译器可能产生的名字并不一样.

typeid形式

typeid有两种形式, 一种在编译期获得类型, 一种在运行时通过RTTI获取类型.

我们先分析编译期就能获得类型, 再分析RTTI获取类型.

编译期获得类型

只要不涉及虚表并且没有基类指针(引用)指向子类的情况都是能在编译期间就可以获得类型的信息.

先来看一下在g++编译器中的基本类型名是什么.

cout << typeid(char).name() << endl;	// c
cout << typeid(short).name() << endl;	// s
cout << typeid(int).name() << endl;		// i
cout << typeid(float).name() << endl;	// f
cout << typeid(double).name() << endl;	// d

添加一个宏定义, 来使不同编译器都能够正常的输出正常的类型.

#include 

#define typeid_name(names) abi::__cxa_demangle(typeid(names).name(), 0, 0, NULL)
// 测试
cout << typeid(char).name() << endl;	// char
cout << typeid(int).name() << endl;		// int

现在就来分析没有虚函数的基类和子类调用时的类型.

#include 
#define typeid_name(names) abi::__cxa_demangle(typeid(names).name(), 0, 0, NULL)
class A {};

class B : public A {};

int main()
{
    A a, *pa;
    B pb;
    pa = &pb;
    cout << typeid_name(A) << endl;		// A
    cout << typeid_name(a) << endl;		// A
    cout << typeid_name(pa) << endl;	// A*
    cout << typeid_name(*pa) << endl;	// A
    cout << typeid_name(pb) << endl;	// B
 
    exit(EXIT_SUCCESS);
}

上式也并没有需要分析的, 接下来我们再来分析RTTI时typeid的不同吧

RTTI时类型

同样使用上面的例子, 但是在基类中添加一个虚函数, 因为有了虚表, 所以在由基类指向子类指针时指针的类型需要在运行时才能确定, 这样也就达到了我们的目的.

#include 
#define typeid_name(names) abi::__cxa_demangle(typeid(names).name(), 0, 0, NULL)

class A 
{
    public:
	virtual void fun() {}
};

class B : public A {};

int main()
{
    A a, *pa;
    B pb;
    pa = &pb;
    cout << typeid_name(A) << endl;		// A
    cout << typeid_name(a) << endl;		// A
    cout << typeid_name(pa) << endl;	// A*
    
    cout << typeid_name(*pa) << endl;	// B
    
    cout << typeid_name(pb) << endl;	// B
 
    exit(EXIT_SUCCESS);
}

有一处发生了改变, typeid_name(*pa)判断的类型居然是B而不是A, 而typeid_name(pa)判断的类型又是A*. 解释如下 :

  1. 这里首先要明白pa*pa所代表的对象. pa它是**A类型的指针**, 指向B类型. 所以单单typeid_name(pa)就是A的指针这是没问题的.

  2. 那么现在来解释typeid_name(*pa). 先要知道基类有虚函数就代表子类跟基类都会有虚表, 指针只能在运行时确定所指向的是哪一个对象的虚表, 而本例pa是指向的是B的虚表, 所以typeid_name(*pa)推导的结果就是B.

以上就可以综合为 : 如果typeid操作符的操作数是至少包含一个虚拟函数的类类型时,并且该表达式是一个基类的引用,则typeid操作符指出底层对象的派生类类型.

总结

typeid需要留意的就是有虚函数存在时, 虚表影响该操作符的类型判断. 其他也就没有什么需要注意的.

你可能感兴趣的:(c/c++,C++基础学习)