C++的多态是通过一张虚函数表(Virtual Table)来实现的,简称为V-Table。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆写的问题,保证其真实反映实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一个地图一样,指明了实际所应该调用的函数。
C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
静态代码
Base *b = new Derive();
b->f();
当编译器看到这段代码的时候,并不知道b的真实身份。编译器能做的就是用一段代码代替这段语句。
1. 明确b类型;
2. 然后通过虚函数表的指针vptr和偏移量,匹配虚函数的入口;
3. 根据入口地址调用虚函数。
- 为什么虚函数必须是类的成员函数
虚函数诞生的目的就是为了实现多态
为什么类的静态成员函数不能为虚函数
- 虚函数,动态绑定,在派生类中可以覆盖;这与静态成员函数的定义本身就是相矛盾的。
typeid dynamic_cast是C++运行时类型信息RTTI(run time type identification)重要组成部分。运行时信息,来自于多态,所以以下运算符只用于基于多态的继承体系中。
typeid
运算符typeid返回包含操作数数据类型信息的type_info对象的一个引用,信息中包括数据类型的名称,要使用typeid,程序中需要包含头文件typeinfo。
int main()
{
cout<<typeid(int).name()<<endl;
return 0;
}
class A
{
public:
virtual ~A(){}
};
class B:public A
{
};
class C:public A
{
};
int main()
{
B b; C c;
A *pa = &b;
if (typeid(pa) == typeid(B))
cout<<typeid(pa).name()<<endl;
if (typeid(*pa) == typeid(B))
cout<<typeid(*pa).name()<<endl;
return 0;
}
dynamic_cast
只用于含有虚函数的父子类中。
是一种运行时的类型转化方式,所以要在运行时作转换判断。检查指针所指类型,然后判断这一类型是否与正在转换成的类型有一种“is a”的关系,如果是,dynamic_cast返回对象地址。如果不是,dynamic_cast返回NULL。
dynamic_cast常用多态继承中,判断父类指针的真实指向。
static_cast: upcast, downcast
dynamic_cast: downcast 是不是得到其目标类型,如果是,则返回其指针,如果不是,返回NULL。