1、什么是虚函数
简单地说:那些被virtual关键字修饰的成员函数就是虚函数。其主要作用就是实现多态性。
多态性是面向对象的核心:它的主要的思想就是可以采用多种形式的能力,通过一个用户名字或者用户接口完成不同的实现。通常多态性被简单的描述为“一个接口,多个实现”。在C++里面具体的表现为通过基类指针访问派生类的函数和方法。看下面这段简单的代码:
class A { public: void print(){cout << "this is A" << endl;} }; class B { public: void print(){cout << "this is B" << endl;} }; int main() { A a; B b; a.print(); b.print(); } |
输出的结果分别是This is A和This is B。但这是否真正做到了多态呢?没有!多态的关键点是用指向基类的指针来调用派生类对象。
int main() { A a; B b; A * p1 = &a; B * p2 = &b; p1->print(); p2->print(); } |
输出的结果是两个This is A。为什么呢?p2明明指向的是class B的对象但却调用class A的print()函数,这不是我们所期望的结果,那么怎么解决这个问题呢?此时就需要虚函数:
class A { public: virtual void print(){cout << "This is A" << endl;} }; class B : public A { public: void print(){cout << "This is B" << endl;} }; |
此刻,class A的成员函数print()已经成了虚函数,那么class B的print()成了虚函数了么?是的!我们只需要把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。所以,class B的print()也成了虚函数。那么对于在派生类的相应函数前是否要用virtual关键字修饰,这个是个人的习惯问题了。
重新运行之前的代码,输出的结果就是This is A和This is B。
简单总结就是:基类的指针在操作派生类对象时,会根据不同的具体对象类型,调用相对应的函数(虚函数).
2、联编
在详细解释虚函数多态是怎么实现之前,我们先了解下联编的概念——就是将模块或者函数合并在一起生成可执行代码的处理过程,同时对每个模块或者函数调用分配内存地址,并且对外部访问也分配正确的内存地址。按照联编所进行的阶段不同,可分为静态和动态两种。
静态联编:在编译阶段就将函数实现和函数调用关联起来称之为静态联编,静态联编在编译阶段就必须了解所有的函数或模块执行所需要的检测信息,它对函数的选择是基于指向对象的指针(或者引用)的类型,
动态联编:在程序执行的时候才进行这种关联称之为动态联编,动态联编对成员函数的选择不是基于指针或者引用,而是基于对象类型,不同的对象类型将做出不同的编译结果。C语言中,所有的联编都是静态联编。C++中一般情况下联编也是静态联编,但是一旦涉及到多态性和虚函数就必须使用动态联编。