c++多态:虚函数表,RTTI,typeid,dynamic_cast

本文介绍了C++中虚函数表的作用,多态如何通过基类指针调用派生类的虚函数,以及dynamic_cast和typeid在处理多态中的类型转换和运行时类型识别的应用。
摘要由CSDN通过智能技术生成

       虚函数表。 

        我们知道要想运用多态技术,必须在基类以及其派生类中必须定义同名的虚函数。而在定义一个类的时候,如果类里面的成员函数有虚函数的话,编译器在编译时都会生成一个隐藏的指针以及一个虚函数表,而这个指针就是指向这个虚函数表的,这个虚函数表里面记录了多个指针,第一个指针指向了一个type_info类类型的常量(记录了这个类的一些信息),其他指针记录了 这个类的虚函数的入口地址。

        下面我们来看一个典型的多态案例:

#include <iostream>
using namespace std;

class animal {

public:
	virtual void eat() {
		cout << "动物在吃东西" << endl;
	}
};

class cat :public animal {

public:
	virtual void eat() {
		cout << "小猫在吃猫粮" << endl;
	}
};

class dog :public animal {

public:
	virtual void eat() {
		cout << "小狗在吃狗粮" << endl;
	}
};

void test(animal &a) {

	a.eat();
}

void main() {

	animal a;
	cat c;
	dog d;

	test(a);//输出:动物在吃东西
	test(c);//输出:小猫在吃猫粮
	test(d);//输出:小狗在吃狗粮
	system("pause");
}

        上面是一个典型的多态运用,代码中,test通过定义一个基类类型的引用作为参数,在程序运行时,通过传入不同类型引用,程序会动态的选择执行对应类中的eat函数。实现这种动态的选择原理是:

        一般我们运用多态时会写类似的如下的代码:

void main() {
	
	animal *a = new cat;

	//或者
	cat b;
	animal &c = b;

	system("pause");
}

        上面两种代码是利用animal类指针/引用指向cat类。写了着这样的代码,在编译的时候,a对象里也会有一个隐藏的指针,这个指针会指向等号右边的类里的虚函数表,也就是cat类里面的虚函数表。那么通过指向的虚函数表就可以找到需要执行的函数入口,就实现了程序在运行时的动态选择。

dynamic_cast。

        上面说了,在运用多态的时候会写如下的类似代码,就是用基类的指针指向其派生类。但是这时如果子类有自己独有的成员函数的时候,想要通过对象a去调用,编译器是不支持的。

#include <iostream>
using namespace std;

class animal {

public:
	virtual void eat() {
		cout << "动物在吃东西" << endl;
	}
};

class cat :public animal {

public:
	virtual void eat() {
		cout << "小猫在吃猫粮" << endl;
	}

	void work() {
		cout << "小猫爱睡觉" << endl;
	}
};

void main() {

	animal *a = new cat;
	a.work();//错误,编译器不支持
}

        这其中的原因我认为没有为什么,就是编译器不支持,语法上的问题,因为即使对象a指向cat类型的内存,但是它定义的是animal类型的指针。这时如果想对cat类的独有成员函数work()进行调用的话,需要进行类型的转换。如下代码所示:

void main() {

	animal *a = new cat;
	//a.work();//调用cat的独有成员函数错误

//c语言风格的强制转换,即使不能转成功也会强制瞎转
	cat *c1 = (cat *)a;

//c++中的正确转行,转换失败会返回空指针
	cat *c2 = dynamic_cast<cat *>(a);
	if (c2 == nullptr)
		cout << "空指针" << endl;
	else
		cout << "转换成功" << endl;


	c2->work();//转换类型后可以调用cat类的独有成员函数

	system("pause");
}

        可以看到有两种转换方式,一种是c语言的风格,强制转换,但是这样做不安全,即使不同的类型也可以转换造成异常不报错,所以一般会使用dynamic_cast来进行类型转换。可以看到转换后就可以对cat的独有成员函数进行调用了。

typeid

        来说说typeid(),可以传入地址/引用/常数,typeid会返回一个常量对象的引用,这个常量对象是一个标准库类型 type_info类型。下面代码举个例子:

void main() {
	
	int a;
	const type_info &inf0 = typeid(a);
	cout << inf0.name() << endl; //输出: int

	const type_info &inf1 = typeid(1.2);
	cout << inf1.name() << endl; //输出: double

	animal *a0;
	const type_info &inf2 = typeid(a0);
	cout << inf2.name() << endl; //输出: class animal *	

	animal *a1 = new cat;
	const type_info &inf3 = typeid(a1);
	cout << inf3.name() << endl; //输出: class animal *	
	const type_info &inf4 = typeid(*a1);
	cout << inf4.name() << endl; //输出: class cat	

	system("pause");
}

typeid可以传入多种不同类型的参数,我估计是运用了函数重载的功能。那为什么自定义的类型也可以识别出来呢?比如上面的cat,animal类。原因如下:

        上面说的写了animal *a = new cat;这样的代码的时候,会有一个隐藏的指针生成,指向等号左边的类型的虚函数表,就是cat类的虚函数表。虚函数表的第一个指针指向一个type_inf的常量对象,里面就记录了一些类的相关信息。所以用typeid()可以将类的信息提取出来:const type_info &inf4 = typeid(*a1);

RTTI

        RTTI,全程run time type idefinitication,就是指程序运行时动态的识别对象的类型。也就是通过typeid()可以做到。

        以上就是对多态学习的一些总结笔记,可以有些是我自己理解的不一定对,但是理论上行得通,暂且这样认为。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值