注:以下操作都是在VS(2013)的32位平台下完成的,各个平台有些许差异
所谓多态,其实就是多种形态
C++中虚函数的主要所用就是为了实现多态,当父类的指针/引用调用重写的虚函数,当父类的指针/引用指向父类时,调用父类的虚函数,指向子类时,调用子类的虚函数. -> 这就是所谓的与类型无关,只与对象有关.
举个栗子:
实现一个购买门票的场景,学生应该买学生票,成人应该买成人票
class Person
{
public:
virtual void BuyTickets()
{
cout << "Person::成人票" << endl;
}
};
class Student : public Person
{
public:
virtual void BuyTickets()
{
cout << "Student::学生票" << endl;
}
};
void func(Person* p)
{
p->BuyTickets();
}
int main()
{
Person per;
Person* p = &per;
func(p);
Student s;
p = &s;
func(p);
return 0;
}
这时候我们希望p指向的是学生,就去调用学生类的BuyTickets函数,p指向的是成人,就去调用成人类的BuyTickets函数
探究虚函数表
虚函数表是通过一块连续内存来存储虚函数的地址。这张表解决了继承、虚函数(重写)的问题。在有虚函数的对象实例中都存在一张虚函数表,虚函数表就像一张地图,指明了实际应该调用的虚函数函数。
下面我们举一个简单的栗子
class A
{
public:
virtual void func1()
{
cout << "A::func1()" << endl;
}
protected:
int _a;
};
class B : public A
{
public:
virtual void func1()
{
cout << "B::func1()" << endl;
}
virtual void func2()
{
cout << "B::func2()" << endl;
}
protected:
int _b;
};
int main()
{
A a;
B b;
return 0;
}
class B 共有继承class A,并且两个类中的func1函数构成多态,class B中还有一个自己的虚函数
我们知道,成员函数都是存在常量区的,虚函数也不例外,只是在类中有一个指针,他指向了一张表的开始,这张表像是一个指针数组,表中的每一个元素都存储了一个地址,也就是当前类中虚函数的地址,数组以NULL结尾(vs中)
下面我们写一个函数将这些虚函数都打印出来
typedef void(*V_FUNC)();
void PrintVTable(int* vtable)
{
printf("vtable : %p\n", vtable);
for (int i = 0; vtable[i] != NULL; ++i)
{
printf("vfunc[%d] : %p ", i, vtable[i]);
V_FUNC f = (V_FUNC)vtable[i];
f();
}
cout << "--------------------------------" << endl;
}
代码中的 typedef void(*V_FUNC)();是定义了一个函数指针,为了便于我们查看打印的结果,在最后通过函数指针调用以下当前函数,打印出对应的结果
下面是完整代码和运行结果
class A
{
public:
virtual void func1()
{
cout << "A::func1()" << endl;
}
protected:
int _a;
};
class B : public A
{
public:
virtual void func1()
{
cout << "B::func1()" << endl;
}
virtual void func2()
{
cout << "B::func2()" << endl;
}
protected:
int _b;
};
typedef void(*V_FUNC)();
void PrintVTable(int* vtable)
{
printf("vtable : %p\n", vtable);
for (int i = 0; vtable[i] != NULL; ++i)
{
printf("vfunc[%d] : %p ", i, vtable[i]);
V_FUNC f = (V_FUNC)vtable[i];
f();
}
cout << "--------------------------------" << endl;
}
int main()
{
A a;
PrintVTable((int*)*((int*)&a));
B b;
PrintVTable((int*)*((int*)&b));
return 0;
}
再举一个复杂一点点的栗子
class A
{
public:
virtual void func1()
{
cout << "A::func1()" << endl;
}
virtual void func2()
{
cout << "A::func2()" << endl;
}
protected:
int _a;
};
class B : public A
{
public:
virtual void func1()
{
cout << "B::func1()" << endl;
}
virtual void func3()
{
cout << "B::func3()" << endl;
}
protected:
int _b;
};
这里的class A类里有两个虚函数,分别为func1,func2,func1被class B的func1重写,class B中也有两个虚函数,func1,func3
下面我们来打印虚表验证一下
上面的操作都是基于32位操作系统的,在64位操作系统下,打印虚表的函数可以修改为
typedef void(*V_FUNC)();
void PrintVTable(int** vtable)
{
printf("vtable : %p\n", vtable);
for (int i = 0; vtable[i] != NULL; ++i)
{
printf("vfunc[%d] : %p ", i, vtable[i]);
V_FUNC f = (V_FUNC)vtable[i];
f();
}
cout << "--------------------------------" << endl;
}
调用方法也应该改为
A a;
PrintVTable((int**)(*((int**)&a)));
B b;
PrintVTable((int**)(*((int**)&b)));