C++语法学习笔记十七:RTTI-dynamic_cast-typeid-虚函数表

实例代码


#include <iostream>
#include <string>
#include <vector>

using namespace std;

//基类

//类定义/ 类声明
class Human{
public:
	Human(){
		std::cout << " 执行了Human::Human() " << std::endl;
	}

	Human(int abc){
		std::cout << " 执行了Human::Human(int) " << std::endl;
	}

	virtual ~Human(){

	}
	void funcpub(){
		std::cout << " 执行了Human::funcpub() " << std::endl;
	}

	void somenamefunc(){
		std::cout << " 执行了Human::somenamefunc() " << std::endl;
	}

	void funchuman(){
		std::cout << " 执行了Human::funchuman() " << std::endl;
	}

	//声明成虚函数
	virtual void eat(){
		std::cout << " 人类吃各种粮食 " << std::endl;
	}

public:
	int m_age; //年龄
	char m_name[100]; //名字

}; // 类定义/ 类声明时 大家千万不要忘记末尾的;


//子类

//男人
class Men : public Human{  //表示Men 是Human的子类
public:
	Men(){
		std::cout << " 执行了Men::Men() " << std::endl;
	}

	Men(int abc){
		std::cout << " 执行了Men::Men(int) " << std::endl;
	}

	void somenamefunc(){

		Human::somenamefunc(); //调用父类somenamefunc函数
		std::cout << " 执行了Men::somenamefunc() " << std::endl;
	}

	virtual void eat(){
		std::cout << " 男人喜欢吃米饭 " << std::endl;
	}

	void testfunc(){
		std::cout << " testfunc " << std::endl;
	}

	//因为友元函数不属于类成员,所以友元函数声明不受public ,protected, private;
	friend void func(const Men& tempmen); //该函数是友元函数

private:
	void funcmen()const {
		std::cout << " 执行了Men::funcmen() " << std::endl;
	}
	//virtual void eat2() = 0; //纯虚函数,没有函数体,只有一个函数声明

public:
	using Human::somenamefunc;

};

//女人
class WoMen : public Human{  //表示Men 是Human的子类
public:
	WoMen(){
		std::cout << " 执行了WoMen::WoMen() " << std::endl;
	}

	WoMen(int abc){
		std::cout << " 执行了WoMen::WoMen(int) " << std::endl;
	}

	void somenamefunc(){
		Human::somenamefunc(); //调用父类somenamefunc函数
		std::cout << " 执行了WoMen::somenamefunc() " << std::endl;
	}

	void funcmen(){
		std::cout << " 执行了WoMen::funcmen() " << std::endl;
	}

	virtual void eat(){
		std::cout << " 女人喜欢吃面食 " << std::endl;
	}

public:
	using Human::somenamefunc;

};


void func(const Men & tmpmen){
	tmpmen.funcmen();
}



int main() {

	//一:RTTI是什么(Run Time Type Identification):运行时类型识别;
	//通过运行时类型识别,程序能够使用基类的指针或者引用来检查这些指针或者引用所指的对象的实际派生类型
	Human *phuman = new Men;
	Human &q = *phuman; // *phuman 表示指针phuman所指向的对象
	//RTTI 我们可以把这个称呼看成是一种系统提供给我们的一种能力,或者一种功能。这种功能或者能力是通过2个运算符来体现;
	//(1) dynamic_cast 运算符:能够将基类的指针或者引用安全的转换为派生类的指针或者引用;
	//(2) typeid运算符:返回指针或者引用所指对象的实际类型。
	//补充:要想让RTTI的两个运算符能够正常工作,那么基类中必须至少要有一个虚函数,不然这两个运算符工作的结果就可能跟我们预测的不一样。
	//因为只要虚函数的存在,这两个运算符才会使用指针或者引用所绑定的对象的动态类型(你new的类型);

	//二:dynamic_cast运算符
	//如果该运算符能够转换成功,说明这个指针实际上是要转换到的那个类型,这个运算符能够帮咱们做安全检查。
	
	Men*p = (Men*)phuman; //用C语言风格的强制类型转换,硬把指针转换成Men*;
	p->testfunc(); // 能够正常的调用Men类的成员函数 testfunc();

	//WoMen *p1 = (WoMen*)(phuman); //强转错误
	//p1->...()

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

	//对于引用,如果用dynamic_cast转换失败,则系统会抛出一个std::bad_cast异常。try{}...catch(){};
	Human &q1 = *phuman;
	try{
		Men &menbm = dynamic_cast<Men&>(q1); //转换不成功,则流程直接进入到catch里边去,如果转换成功,流程继续往下走。
		//走到这里,表示转换成功
		cout << "phuman 实际上是一个Men类型" << endl;
		//在这里调用类Men的成员函数都是安全的。
		menbm.testfunc();
	}
	catch (std::bad_cast)
	{
		cout << "phuman实际不是一个Men类型" << endl;
	}

	//三:typeid运算符
	//typeid(类型【指针/引用】);也可能typeid(表达式)
	//拿到对象类型信息,typeid就会返回一个常量对象的引用,这个常量对象 是一个标准库类型 type_info(类/类类型)。

	Human &q2 = *phuman;
	cout << typeid(*phuman).name() << endl;  // class Men
	cout << typeid(q2).name() << endl;  // class Men

	char a[10] = { 5, 1 };
	int b = 120;

	//cout << typeid(a).name << endl;   // char [10]
	//cout << typeid(b).name << endl;   // int
	//cout << typeid(19.6).name << endl;   // double
	//cout << typeid("asd").name << endl;   // char cosnt[4]

	//typeid主要是为了比较两个指针是否指向同一种类型的对象;
	//(1) 两个指针定义的类型相同(Human),不管他们new的是啥,typeid都相等。
	Human *phuman11 = new Men;
	Human *phuman22 = new WoMen;
	if (typeid(phuman11) == typeid(phuman22)) {
		cout << "phuman11 和 phuman22 是同一种类型【看指针定义】" << endl;
	}

	//比较对象时,看的是new出来的是哪个对象或者该指针指向的是哪个对象,和定义该指针时定义的类型没关系。
	Human *phuman33 = new Men;
	Men* pmen33 = new Men;
	Human *phuman44 = pmen33;
	if (typeid(*phuman33) == typeid(*pmen33)) //成立 ,千万不要把 * 拉下
	{
		cout << "phuman33 和 pmen33指向的对象类型相同" << endl;
	}
	if (typeid(*pmen33) == typeid(*phuman44)) //成立
	{
		cout << "pmen33 和 phuman44 指向的对象类型相同" << endl;
	}


	Human *phuman55 = new Men;
	if (typeid(*phuman55) == typeid(Men)) //成立
	{
		cout << "phuman55 指向 Men" << endl;
	}
	else if (typeid(*phuman55) == typeid(Human))
	{
		cout << "phuman55 指向 Human" << endl;
	}

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

	//四:type_info类
	//typeid 就会返回一个常量对象的引用,这个常量对象时一个标准库类型 type_info(类/类类型)
	//a). name : 名字 : 返回一个C风格字符串。
	Human *phuman66 = new Men;
	const type_info &tp = typeid(*phuman66);
	cout << tp.name() << endl; //class Men

	//b). ==, !=
	Human *phuman77 = new Men;
	const type_info &tp2 = typeid(*phuman77);
	if (tp == tp2)
	{
		cout << "tp 和 tp2 类型相同" << endl;
	}

	Human *phuman88 = new WoMen;
	const type_info &tp3 = typeid(*phuman88);
	if (tp == tp3)
	{
		cout << "tp 和 tp3 类型相同" << endl;
	}

	//五: RTTI与虚函数表
	//C++中,如果类里含有虚函数。编译器就会对该类产生一个虚函数表。
	//虚函数表里有很多项,每一项都是一个指针,每个指针指向的是这个类里的各个虚函数的入口地址。
	//虚函数表项里,第一个表项很特殊,它指向的不是虚函数的入口地址,它指向的实际上是咱们这个类所关联的type_info对象。
	
	Human *phuman99 = new Men;
	const type_info &tp4 = typeid(*phuman99);

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



	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值