现在的位置: 首页 > 综合 > 正文

C++多态性原理

2018年05月27日 ⁄ 综合 ⁄ 共 1529字 ⁄ 字号 评论关闭

C++多态性
    两种表现形式:静态多态性 通过一般的函数重载来实现。
                             动态多态性 通过虚函数来实现。

    静态多态性比较简单,主要动态多态性比较难理解。

    动态多态性有两个条件:
    1、在基类中必须使用虚函数、纯虚函数
   2、调用函数时要使用基类的指针或引用。

    只要在基类的成员函数前加上virtual,该成员函数就是虚函数,从基类派生出来的类的同名成员函数,不管前面是否有virtual,同样是虚函数,在虚函数的实现时,前面不能加virtual。

    内联函数不能是虚函数,因为内联函数不能在运行时动态确定位置,即使虚函数在类的内部定义,但在编译的时候仍然看做是非内联的。
    只有类的成员函数才能声明为虚函数,普通函数不能声明为虚函数。
    静态成员函数不能是虚函数。

    虚函数的原理:

     普通函数的处理:
     一个特定的函数都会映射到特定的代码,无论时编译阶段还是连接阶段,编译器都能计算出这个函数的地址,调用即可。

    虚函数的处理:
    被调用的函数不仅依据调用的特定函数,还依据调用的对象的种类。通常是由虚函数表(vtable)来实现的。

    虚函数表的结构:
    它是一个函数指针表,每一个表项都指向一个函数。任何一个包含虚函数的类都会有这样一张表。需要注意的是vtable只包含虚函数的指针,没有函数体。实现上是一个函数指针的数组。

    虚函数表既有继承性又有多态性
    每个派生类的vtable继承了它各个基类的vtable,如果基类vtable中包含某一项,则其派生类的vtable中也将包含同样的一项,但是两项的值可能不同。如果派生类重载(override)了该项对应的虚函数,则派生类vtable的该项指向重载后的虚函数,没有重载的话,则沿用基类的值。

    每一个类只有唯一的一个vtable,不是每个对象都有一个vtable,每个对象都有一个指针,这个指针指向该类的vtable(当然,前提是这个类包含虚函数)。

    那么,每个对象只额外增加了一个指针的大小,一般说来是4字节。
    在类对象的内存布局中,首先是该类的vtable指针,然后才是对象数据。

    在通过对象指针调用一个虚函数时,编译器生成的代码将先获取对象类的vtable指针,然后调用vtable中对应的项。

    对于通过对象指针调用的情况,在编译期间无法确定指针指向的是基类对象还是派生类对象,或者是哪个派生类的对象

    但是在运行期间执行到调用语句时,这一点已经确定,编译后的调用代码能够根据具体对象获取正确的vtable,调用正确的虚函数,从而实现多态性。

下面是通过基类的指针来调用虚函数时,所发生的一切:
step 1:开始执行调用 pA->run();(pA为基类指针,这里能判断到底是哪个对象)
step 2:取得对象的vtable的指针
step 3:从vtable那里获得函数入口的偏移量,即得到要调用的函数的指针
step 4:根据vtable的地址找到函数,并调用函数。
step 1和step 4对于一般函数是一样的,虚函数只是多了step 2和step 3。

   基类和派生类是共用一表,还是各有各的表(物理上)?

   答:基类和派生类是各有各的表,也就是说他们的物理地址是分开的,基类和派生类的虚表的唯一关联是:当派生类没有实现基类虚函数的重载时,派生类会直接把自己表的该函数地址值写为基类的该函数地址值.

来自: http://hi.baidu.com/scuxy06/blog/item/5a8140d61e5dbb2507088b5c.html

抱歉!评论已关闭.