通常,编译器处理虚函数的一种方法是,对于每个类(基类和继承类),创建一个虚函数地址表(数组),其中按顺序存储了该类所有虚函数的地址。如果继承类对基类虚函数进行了重新定义,则继承类的虚函数地址表相应的地址改为新定义的函数的地址,否则将继续使用基类函数的地址。如果继承类创建了新的虚函数(基类中没有定义的),那么将会将这个虚函数地址加入继承类的虚函数地址表中。对于每个类创建的对象,都有一个隐藏的成员,成员中存储的是指向所属类的虚函数地址表的地址。动态联编时,到表中查找地址,然后执行相应的代码块。
总的来说,使用虚函数,在内存和执行速度上都会有一定的成本,包括:
- 每个对象都会增大,增大量为存储地址的空间(存储了一个指向虚函数地址表的指针)
- 对于每个类,编译器都要创建一个虚函数地址表(数组)
- 对于每个函数调用,都要执行一个额外操作,即到表中查找地址
图解: