RTTI、dynamic_cast、typeid、虚函数表

一、RTTI

RTTI(Run-Time Type Identification),通过运行时类型信息,程序能够使用基类指针或引用检查这些指针或引用所指的对象的实际派生类

Human* phuman = new Man;

Human &a = *phuman;  // *phuman表示指针phuman所指向的对象

RTTI我们可以把这个程序看成是一种系统提供给我们的一种能力,或者一种功能。这种功能或者能力是通过2个运算符来体现:

(1)dynamic_cast运算符:能够将将基类的指针或者引用安全的转换为派生类的指针或者引用

(2)typeid运算符:返回指针或者引用所指对象的实际类型

补充:想让RTTI两个运算符能够过正常工作,基类中必须至少要有一个虚函数,不然这两个运算符工作的结构就可能跟我们预期不一致。因为只有虚函数的存在,这两个运算符才会使用指针或者引用所绑定的对象的动态类型。

二、dymamic_cast运算符

如果该运算符能够转换成功,说明这个指针实际上是要转换到的那个类型。这个运算符能够帮咱们做安全检查

Human* phuman = new Men;
Men* p = (Men*)(phuman); // 用c语言风格的强制类型转换,硬把指针转换成Men*;
p->testfunc(); // 能够正常的调用Men类的成员函数testfunc();

2.1指针

Human* phuman  = new Men;
Men* pmen = dynamic_cast<Men*>(phuman);
if (pemn != nullptr) {
    cout << "phuman实际是一个Men类型" << endl;
    // 在这里操作类Men里边的成员函数,成员变量都能够操作并且安全的操作
    pem->testfunc();
} else {
    // 转换失败
    out << "phuman不是一个Men类型" << endl;
}

2.2引用

如果用dynamic_cast转换失败,则系统会抛出一个std::bad_cast异常try{}catch(){}

Human* phuman  = new Men;
Human &q = *phuman; // 这就是引用
try {
    Men& menbm = dynamic_cast<Men&>(q);
    // 转换成功
     cout << "phuman实际是一个Men类型" << endl;
    // 在这里操作类Men里边的成员函数,成员变量都能够操作并且安全的操作
    menbm.testfunc();
} catch(std::bad_cast) {
    // 转换失败
    out << "phuman不是一个Men类型" << endl;
}

三、typeid运算符

typeid(类型【指针/引用】;也可能typeid表达式

拿到对象类型信息;typeid就会赶回一个常量对象的引用,这个常量对象是一个标准库类型type_info(类/类类型)

Human* phuman = new Men;
Human& q = *phuman;
cout << typeid(*phuman).name() << endl; // class Men;
cout << typeid(q).name() << endl; // class Men
char a[10] = {5, 1};
int b = 666;
cout << typeid(a).name() << endl; // char[10]
cout << tyepid(b).name() << endl; // int
cout << tyepid(19.6).name() << endl; // double
cout << tyepid("asd").name() << endl; // char const[4]

typeid主要是为了比较两个指针是否指向同一种类型的对象

(1)两个指针定义的类型相同(Human),不管他们new的是啥,typeid都相等。

该例不太符合我们的期盼和要求

Human* phuman = new Men;
Human* phuman2 = new Women;
if (typeid(phuman) == typeid(phuman2)) { // 成立
    cout << "phuman和phuman2是同一种类型[看指针定义]" << endl;
}

比较对象时,看的是new出来的是哪个对象或者该指针指向的是哪个对象,和定义该指针时定义的类型没关系。

Human* phuman = new Men;
Human* phuman2 = new Men;
Human* phuman3 = phuman2; 
if (typeid(*phuman) == typeid(*phuman2)) { // 成立
    cout << "phuman和phuman2指向的对象类型相同" << endl;
}
if (typeid(*phuman) == typeid(*phuman3)) { // 成立
    cout << "phuman2和phuman3指向的对象类型相同" << endl;
}

基类必须要有虚函数,否则上边的条件不成立。

切记:只有当基类有虚函数时,编译器才会对typeid()中的表达式求值。否则如果某个类型不含有虚函数,则typeid()返回的是表达式的静态类型(定义时的类型),既然是定义的类型,编译器就不需要对表达式求值,也能知道表达式的静态类型。

四、type_info类

typeid机会返回一个常量对象的引用,这个常量对象是一个标准库;类型type_info(类/类类型)

a)name:名字返回一个c风格字符串

Human* phuman = new Men;
const type_info& tp = typeid(*phuman);
// cout << tp.name() << endl; // class Men

b)==,!=

Human* phuman2 = new Men;
const type_info& tp2 = typeid(*phuman2);
if(tp == tp2) {    // 成立
    cout << "类型相同" << endl;
}
Human* phuman3 = new Women;
const type_info& tp3 = typeid(*phuman3);
if(tp == tp3) {    // 不成立
    cout << "tp和tp3类型相同" << endl;
}

五、RTTI与虚函数表

c++中,如果类里含有虚函数。编译器就会对该类产生一个虚函数表

虚函数里有很多项,每一个项都是一个指针。每个指针指向的是这个类里的各个虚函数的入口地址。

虚函数表项里,第一个表象很特殊,它指向的不是虚函数的入口地址,它指向的实际上是咱们这个类所关联的type_info对象。

Human* phuman = new Men;
const type_info& tp = typeid(*phuman);

phuman对象里有一个我们看不见的指针,这个指针指向是这个对象所在的类Men里的虚函数表

参考:https://blog.csdn.net/INGNIGHT/article/details/72372968

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值