当涉及到C++中的多态性和继承时,RTTI(Run Time Type Identification)是一个非常重要的概念。RTTI允许程序在运行时识别对象的实际类型,即使是通过基类的指针或引用进行操作。
RTTI是什么(Run Time Type Identification):运行时类型识别;
通过运行时类型识别,程序能够使用基类的指针或者引用来检查这些指针或者引用所指的对象的实际派生类型。
Human* phuman = new Men;
Human& q = *phuman;//*phuman表示指针phuman所指向的对象
RTTI我们可以把这种称呼看成一种系统提供给我们的一种能力,或者一种功能。这种功能或者能力是通过2个运算符来体现的。
dynamic_cast
:安全地将基类指针或引用转换为派生类指针或引用。typeid
:返回指针或引用所指对象的实际类型。补充:要想能正常工作RTTI的两个运算符能正常工作,基类中至少必须要有一个虚函数,不然的话,这两个运算符工作的结果,更我们预想结果的不一样,因为只有虚函数的存在,这个两个运算符才会使用指针或者引用所绑定的对象的动态类型(你new的类型);
dynamic_cast
可以安全地将基类指针或引用转换为派生类指针或引用,从而进行安全检查。当转换成功时,可以使用转换后的指针或引用进行操作,否则会返回nullptr
(对于指针)或抛出std::bad_cast
异常(对于引用)。
Human* phuman = new Men;
Men* p = (Men*)(phuman);
p->testfunc();//能够正常调用Men类的成员函数testfunc();
Women* p1 = (Women*)(phuman);
p1->...() 不安全
class Men : public Human//表示Men是Human的子类
//class Men : protected Human
//class Men : private Human
{
public:
void testfunc()
{
std::cout << "testfunc" << std::endl;
}
};
Human* phuman = new Men;
Men* pmen = dynamic_cast<Men*>(phuman);
if (pmen != nullptr) {
std::cout << "phuman实际是一个Men类型" << std::endl;
// 在这里操作类Men里边的成员,成员变量能够操作并且安全
pmen->testfunc();
} else {
// 转换失败
std::cout << "phuman不是一个Men类型" << std::endl;
}
对于引用,如果用dynamic_cast
转换类型,则系统会抛出一个std::bad_cast
异常。
class Men : public Human//表示Men是Human的子类
//class Men : protected Human
//class Men : private Human
{
public:
void testfunc()
{
std::cout << "testfunc" << std::endl;
}
};
Human* phuman = new Men;
Human& q = *phuman;
try {
Men& menbm = dynamic_cast<Men&>(q);
std::cout << "phuman实际上是一个Men类型" << std::endl;
// 在这里调用类Men的成员函数都是安全的。
menbm.testfunc();
} catch (std::bad_cast) {
std::cout << "phuman实际不是一个Men类型" << std::endl;
}
typeid运算符可以返回一个常量对象的引用,这个常量对象是一个标准类型type_info,它包含有关类型的信息。typeid可以用于获取对象的实际类型,比较两个指针指向的对象类型等。
typeid(类型):也可能typeid(表达式)
拿到对象信息;typeid就会返回一个 常量对象的引用,这个常量对象是一个标准类型 type_info(类/类型)\
Human* phuman = new Men;
Human& q = *phuman;
std::cout << typeid(*phuman).name() << std::endl; // 输出:class Men
std::cout << typeid(q).name() << std::endl; // 输出:class Men
char a[10] = { 2,1 };
int b = 1;
std::cout << typeid(a).name() << std::endl; // 输出:char [10]
std::cout << typeid(b).name() << std::endl; // 输出:int
std::cout << typeid(1.23).name() << std::endl; // 输出:double
std::cout << typeid("end").name() << std::endl; // 输出:char const[4]
**typeid主要用于比较两个指针是否指向相同类型的对象。**当比较对象时,typeid会考虑对象的实际类型,而不是指针或引用的类型。
typeid 主要是为了比较两个指针是否指向同一个类型的对象
(1)两个指针定义的类型相同(Human),不管new的类型是什么,typeid都相等
该事例不符合咱们期盼和要求
/*Human* phuman = new Men;
Human* phuman2 = new Women;
if (typeid(phuman) == typeid(phuman2)) {
std::cout << "phuman和phuman2是同一种类型(看指针定义,不看new后面的类型)" << std::endl;
}*/
比较对象时,看的是new出来的是哪个对象或该指针指向哪个对象,和定义该指针时定义的类型没关系
Human* phuman = new Men;
Men* phuman2 = new Men;
Human* phuman3 = phuman2;
if (typeid(*phuman) == typeid(*phuman2)) {//成立,千万不要把*忘记
std::cout << "phuman和phuman2指向的对象类型相同"<<std::endl;
}
if (typeid(*phuman2) == typeid(*phuman3)) {//成立
std::cout << "phuman2和phuman3指向的对象类型相同" << std::endl;
}
Human* phuman = new Men;
if (typeid(*phuman) == typeid(Men)) {//成立
std::cout << "phuman指向Men" << std::endl;
}
基类必须有虚函数,否则typeid比较条件不成立。
只有当基类有虚函数时,编译器才会对typeid中的表达式求值。否则,如果某个类型不含虚函数,则typeid返回的是表达式的静态类型(定义时的类)。
**在C++中,typeid运算符可以返回一个常量对象的引用,这个常量对象是一个标准库类型type_info,它包含有关类型的信息。我们可以使用type_info对象的name()方法来获取类型的名称。**以下是一个示例:
#include
#include
class Human {
public:
virtual ~Human() {}
};
class Men : public Human {
// ...
};
int main() {
Human* phuman = new Men;
const std::type_info& tp = typeid(*phuman);
std::cout << tp.name() << std::endl; // 输出:class Men
}
**在C++中,如果类包含虚函数,编译器就会为该类生成一个虚函数表。虚函数表中存储了指向虚函数入口地址的指针。虚函数表的第一个表项通常指向type_info对象,用于存储对象的实际类型信息。**以下是一个示例:
Human* phuman = new Men;
const std::type_info& ty = typeid(*phuman);
// phuman对象里有一个我们看不见的指针,这个指针指向这个对象所在的类Men的虚函数表
完整示例代码
#include
#include
class Human {
public:
virtual ~Human() {}
};
class Men : public Human {
// ...
};
class Women : public Human {
// ...
};
int main() {
Human* phuman = new Men;
const std::type_info& tp = typeid(*phuman);
std::cout << tp.name() << std::endl; // 输出:class Men
Human* phuman2 = new Women;
const std::type_info& tp3 = typeid(*phuman2);
if (tp == tp3) {
std::cout << "tp和tp3类型相同" << std::endl;
}
const std::type_info& ty = typeid(*phuman);
// phuman对象里有一个我们看不见的指针,这个指针指向这个对象所在的类Men的虚函数表
return 0;
}
RTTI提供了一种在运行时获取对象实际类型信息的机制,使得C++的多态性和继承更加灵活和强大。通过dynamic_cast运算符、typeid运算符和type_info类,我们可以进行安全的类型转换和类型比较,而虚函数表则为RTTI提供了基础支持。这些工具和机制为C++程序员提供了更多的编程灵活性和安全性。
希望这篇博客能够帮助你更好地理解RTTI在C++中的应用。