虚函数可以在运行的过程中动态编联,根据指针所指的对象,调用对应的函数
成为虚函数必须满足两个条件:
1.函数依赖于对象调用:因为虚函数是存储在虚函数表中,有一个虚函数指针指向虚函数表,要调用虚函数必须通过虚函数指针,虚函数指针是存储在对象中的。
2.函数必须可寻址,因为虚函数表中存放的是虚函数的入口地址
不能成为虚函数的函数
1.内联函数:在内联函数的调用点将其展开,不能产生函数符号,所以不能把它存入虚表
2.构造函数:只有在调用完构造函数之后才能生成对象,虚函数依赖于对象的调用,要是将构造函数设为虚函数的话就不能生成对象了
3.静态函数:静态函数依赖于类,不依赖于对象,所以不能存放在虚表中
可以设为虚函数的函数:
1.普通的成员函数
2.析构函数:因为在调用析构函数之前已经建立了对象,并且析构函数可以取地址
析构函数必须设为虚函数的情况:
基类指针指向了派生类的对象时,必须将基类的析构函数设为虚函数。
编译器会将虚函数表的指针存放在对象实例中最前面的位置,通过对象实例的地址就可以得到虚函数指针的地址,通过遍历虚函数指针就可以调用虚函数
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
};
typedef void(*Fun)(void);
Base b;
Fun pFun = NULL;
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
// Invoke the first virtual function
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
输出:
虚函数表地址:0012FED4
虚函数表 — 第一个函数地址:0044F148
Base::f
通过强行把&b转成int *,*取得虚函数表的地址,然后,再次取址就可以得到第一个虚函数的地址了,也就是Base::f()
调用Base::g()和Base::h(),其代码如下:
(Fun)*((int*)*(int*)(&b)+0); // Base::f()
(Fun)*((int*)*(int*)(&b)+1); // Base::g()
(Fun)*((int*)*(int*)(&b)+2); // Base::h()
·是结束标志
参考blog主要看图
主要包括单继承无覆盖、单继承覆盖、多重继承、多重继承有覆盖的情况。