zoukankan      html  css  js  c++  java
  • 单继承下的虚表布局

    在C++中,多态表示 “以一个公有基类的指针或引用,寻址出一个派生类对象” 。

    假如有调用 ptr->get_c() ,其中ptr是基类指针,get_c()是一个虚函数。要在执行期能正确调用get_c()的实例,我们需要知道:

         1.ptr所指对象的真正类型,以便我们选择正确的get_c()实例。

         2.get_c()实例的位置,以便我们能够调用他。

    在实现上,编译时期会构建出来一张虚表,表格中有程序的虚函数的执行期地址。

                  为了找到这个表格,每一个类对象被安插一个由编译器产生的指针,指向虚表。

                  为了找到函数地址,每一个虚函数被指派一个表格索引值。

    如果有代码例子:

    class A
    {
    protected:
        int a;
    public:
        A(const int a) :a(a){}
        virtual ~A();
        virtual int pure_v() = 0; //纯虚函数
        virtual int get_b(){ return 0; }
        virtual int get_c(){ return 0; }
    };
    class B :public A
    {
    protected:
        int b;
    public:
        B(int a = 0, int b = 0):A(a),b(b){}
        ~B();
        int pure_v();
        int get_b(){ return b; }
    };
    class C :public B
    {
    protected:
        int c;
    public:
        C(int a = 0, int b = 0, int c = 0) :B(a, b), c(c){}
        ~C();
        int pure_v();
        int get_c(){ return c; }
    };

    一个类只会有一张虚表。每个表内含其对应类对象中三类虚函数,这三类包括:

        1.这一类所定义的虚函数实例或改写自基类的虚函数实例。如例子中Calss B中的get_b()。

        2.继承自基类的已被改写过的函数实例。如例子中Calss C的虚表中会有get_b(),在C中不改写,而在B中已被改写过。

        3.一个纯虚函数的实例。如Class B中的pure_v()。

    接下来我们看类A,B,C的虚表布局。

    对于A a; 布局如下:(博主是用visio画的,有点丑- -)

    _vptr是编译时期产生的虚表指针。

    slot 0通常是指出每个类所关联的type_info object(用以支持RTTI)。

    A的虚析构被指派为solt 1。纯虚函数被指派为solt 2,但A中的纯虚函数没有定义,如果意外调用了此函数,通常的操作是结束这个程序。

    get_b()被指派slot ,get_c()被指派slot 4。

    对于B:A b; 布局如下:

    B的虚表中在solt 1指出析构函数。A中的纯虚函数有了定义,所以在slot 2指出pure_()。

    自己的get_b()函数实例地址放在solt 3。继承自A的get_c()函数实例地址放在solt 4。

    对于C:B c; 布局如下:

    solt 1放置析构函数地址,solt 2放置pure_v()函数地址,solt 3放置继承自B的get_b()的函数地址,solt 4放置自己的get_c()函数地址。

    现在再来看调用ptr->get_c();

    *在每次调用get_c()时,我们并不知道ptr所指对象的真正类型。但是我知道通过ptr可以存取到该对象的虚表。

    *虽然我不知道哪一个get_c()函数实例会被调用,但是我知道每一个get_c()的函数实例地址都被放在solt 4中。

    通过这些信息,编译器可以把调用改写为:

                                                       (ptr->_vptr[4])(ptr);  

    从而在执行期调用正确的函数实例。

  • 相关阅读:
    洛谷 P2678 跳石头
    洛谷 P1145 约瑟夫
    LibreOJ #515. 「LibreOJ β Round #2」贪心只能过样例
    洛谷 P2966 [USACO09DEC]牛收费路径Cow Toll Paths
    网络编程 --- TCP
    进程
    并发编程
    网络编程 --- UDP
    网络编程
    面向对象编程 --- 反射
  • 原文地址:https://www.cnblogs.com/zjc0202/p/4520162.html
Copyright © 2011-2022 走看看