C++虚函数与虚函数表

来源:互联网 发布:淘宝二手哑铃 编辑:程序博客网 时间:2024/06/01 07:30

       C++有个玩意叫虚函数,它的样子和普通的函数基本一样,只不过在函数前面加一个virtual关键字。它是干什么用的呢?先上一段简短的代码,结合代码来代来说明,例如有一个类People:

class People{public:People(string name){this->name = name;}virtual void showName(){cout << "The People's name is " << name << endl;}protected:string name;};

       然后有个类Student继承它:

class Student : public People{public:Student(string name):People(name){}};
       如果我创建一个People类的指针,让它指向一个Student类的对象,用该指针调用showName:

People *people = new Student("Kenney Qin");people->showName();

       它就会打印出:The People's name is Kenney Qin

       如果我把Student类也写一个形式和People类中一样的函数showName:

class Student : public People{public:Student(string name):People(name){}void showName(){cout << "The Student's name is " << name << endl;}};

       这时再运行刚才的代码,打印出来的是:The Student's name is Kenney Qin

 也就是说,它会先去看子类有没有覆盖了这个虚函数,如果覆盖了,那么就调用子类的,否则就调用基类的,这样就很方便,可以为基类的一些函数提供默认的实现,如果用户想自己去实现,那就会调用用户自己实现的,如果用户不想自己去实现,那就调用默认的。这里注意一点,必须用指针去调用函数,才能有这种效果,如果是用类的对象去调用,就不行,例如下面这样:

People people = Student("Kenney Qin");people.showName();
       那么它就不会调用Student里的showName,运行结果是打印出:The People's name is Kenney Qin

 虚函数是C++中实现多态性的手段,而多态性又是面向对象编程的重要特性,由此可见虚函数的重要性。既然它这么重要,我们不仅要学会如何使用,更要知道其原理,下面说说它是如何实现的。

 首先有个概念,叫“虚函数表”,它是用来记录类中所声明的虚函数的地址,不仅基类会有一张虚函数表,派生类也有一张,而且如果派生类中没有覆盖基类中的虚函数时,派生类的虚函数表中的虚函数的地址就和基类中的一样,下面来看一张图,引自《C++ Primer》:


 上图中,有两个类,Scientist类和Physicist类,每个类分别有一张虚函数表,虚函数表的地址是作为一个隐藏的成员存储在类中,这样类的指针就能找到虚函数表在哪,进而从表中找到虚函数的地址,Scientist类和Physicist类的虚函数表的地址分别是2008和2096。在Scientist类中,有两个虚函数,show_name和show_all,在Scientist类的虚函数表中就会记录这两个虚函数的地址,在上图中分别是4064和6400。而在Physicist类中,show_all覆盖了基类的虚函数show_all,并且增加了一个新的虚函数show_field,那么Physicist类的虚函数表中所记录的就是3个地址,分别是从基类中继承而来的虚函数show_name的地址,覆盖了基类中虚函数show_all的那个函数的地址(也就是Physicist类中的show_all的地址),以及show_field的地址。因此,在Physicist类中,show_name函数的地址和基类是一样的,而由于覆盖基类中的show_all虚函数,所以Physicist类中的show_all函数有一个新的地址,此时这里的show_all函数和基类中的show_all就不一样了,它们完全可以有不同的实现。图的最下面是一个调用的例子,把一个Scientist指针指向一个Physicist对象,然后用该指针调用show_all,到底是调用基类的show_all还是派生类的show_all呢?那么就去看虚函数表,是看Scientist类的虚函数表还是看Physicist类的虚函数表呢?是看Physicist类的虚函数表,为什么类?虽然指针是Scientist类,它但指向的是一个Physicist类的对象,指针本身只存储一个地址而已,类的信息都存储在Physicist类的对象那里,所以自然是Physicist类的虚函数表。因此从Physicist的成员中找到虚函数表的地址2096,然后在地址2096处找到虚函数表,从表中找到show_all函数的地址,是6820,不知道大家有没有发现,虚函数表中的虚函数是按在类中声明的顺序排下来的,先排基类,然后排派生类,如果有覆盖就直接覆盖,换成新的地址,否则就继续往后排。找到了show_all函数的地址6820后,就可以跳到这个地址去执行这个函数了~




0 0
原创粉丝点击