C++的三大特性之一多态,分为静态联编和动态联编,在编译过程中进行联编称为静态联编,编译器必须生成能够在程序运行时选择正确的代码,称为动态联编。
虚函数是动态联编的关键,使用virtual关键字创建虚函数,在派生类中进行重写。发生动态绑定的条件是:
1、只有被定义为虚函数的成员函数才能在派生类重写(不是重载),非虚函数不能进行动态绑定。
2、必须通过基类类型的指针或引用进行函数调用
一个类中的虚函数的地址都会储存在虚表中,所以虚表里存的是一个个地址,而类中的虚指针指向虚表,在发生动态绑定时基类虚函数的地址会被替换成子类重写函数的地址。
派生类会继承基类的虚表,父类和子类都有自己的虚表指针,有自己的虚表。
#include<iostream>
using namespace std;
class A {
public:
virtual void vfun1() {
cout << "vfun1 base" << endl;
}
virtual void vfun2() {
cout << "vfun2 base" << endl;
}
void fun1();
void fun2();
private:
//int m_data, m_data;
};
class B :public A {
public:
void vfun1() {
cout << "vfun1 son" << endl;
}
/*void vfun2() {
cout << "vfun2 son" << endl;
}*/
void fun1() {
cout << "son_fun1" << endl;
}
};
class C :public B {
public:
void vfun2() {
cout << "C_vfun2" << endl;
}
void fun2();
};
int main() {
//当父类指针指向子类地址
//在类创建时会发生动态绑定
//虚函数在子类重写,父类虚表的函数被子类重写的函数覆盖
//调用的是子类的重写函数
A* a = new B;
a->vfun1();
//不发生动态绑定,没有实例化派生类,虚表地址没有更改
A* b = new A;
b->vfun1();
//可直接通过虚表调用函数
//通过函数指针调用
typedef void(*p)();
//调用虚函数vfun1()
//*(int*)a将指针类型转化为四字节的,得到虚表地址
((p)(*((int*)*(int*)a + 0)))();
//调用虚函数vfun2(),没有被重写
((p)(*((int*)*(int*)a + 1)))();
return 0;
}