zoukankan      html  css  js  c++  java
  • 第51课 C++对象模型分析(下)

    继承对象模型:

    继承对象模型初探:

    结构体与类等价的示例:

    可以看到mi、mj、mk被改变了,这三个值在外界本来是访问不到的,但是现在通过一个指针却改变了。

    这说明本例中结构体的内存分布和继承类对象的内存分布是一样的。

    父类的成员排在前面,继承类新添加的成员排在后面。

    多态对象模型:

    多态本质分析小实验:

    运行结果如下:

    可以看到定义了虚函数后,类的大小增加了4个字节,这说明确实多了一个虚函数表指针。

     

     测试虚函数表指针的位置:

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 class Demo
     7 {
     8 protected:
     9     int mi;
    10     int mj;
    11 public:
    12     virtual void print()
    13     {
    14         cout << "mi = " << mi << ", "
    15              << "mj = " << mj << endl;
    16     }
    17 };
    18 
    19 class Derived : public Demo
    20 {
    21     int mk;
    22 public:
    23     Derived(int i, int j, int k)
    24     {
    25         mi = i;
    26         mj = j;
    27         mk = k;
    28     }
    29     
    30     void print()
    31     {
    32         cout << "mi = " << mi << ", "
    33              << "mj = " << mj << ", "
    34              << "mk = " << mk << endl;
    35     }
    36 };
    37 
    38 struct Test
    39 {
    40     void* p;
    41     int mi;
    42     int mj;
    43     int mk;
    44 };
    45 
    46 int main()
    47 {
    48     cout << "sizeof(Demo) = " << sizeof(Demo) << endl;         
    49     cout << "sizeof(Derived) = " << sizeof(Derived) << endl;  
    50     
    51     Derived d(1, 2, 3);
    52     Test* p = reinterpret_cast<Test*>(&d);
    53     
    54     cout << "Before changing ..." << endl;
    55     
    56     d.print();
    57     
    58     p->mi = 10;
    59     p->mj = 20;
    60     p->mk = 30;
    61     
    62     cout << "After changing ..." << endl;
    63     
    64     d.print();
    65     
    66     return 0;
    67 }

     运行结果:

    打印结果正确,这说明虚函数表的指针确实在开头的位置。要不然,我们重解释内存后,在结构体中修改的mi、mj、mk和类对象中的mi、mj、mk是不对应的。

    用C写面向对象,并实现多态:

     1 #ifndef _51_2_H_
     2 #define _51_2_H_
     3 
     4 typedef void Demo;
     5 typedef void Derived;
     6 
     7 Demo* Demo_Create(int i, int j);
     8 int Demo_GetI(Demo* pThis);
     9 int Demo_GetJ(Demo* pThis);
    10 int Demo_Add(Demo* pThis, int value);
    11 void Demo_Free(Demo* pThis);
    12 
    13 Derived* Derived_Create(int i, int j, int k);
    14 int Derived_GetK(Derived* pThis);
    15 int Derived_Add(Derived* pThis, int value);
    16 
    17 #endif
      1 #include "51-2.h"
      2 #include "malloc.h"
      3 
      4 static int Demo_Virtual_Add(Demo* pThis, int value);
      5 static int Derived_Virtual_Add(Demo* pThis, int value);
      6 
      7 struct VTable     // 2. 定义虚函数表数据结构
      8 {
      9     int (*pAdd)(void*, int);   // 3. 虚函数表里面存储什么???
     10 };
     11 
     12 struct ClassDemo
     13 {
     14     struct VTable* vptr;     // 1. 定义虚函数表指针  ==》 虚函数表指针类型???
     15     int mi;
     16     int mj;
     17 };
     18 
     19 struct ClassDerived
     20 {
     21     struct ClassDemo d;
     22     int mk;
     23 };
     24 
     25 static struct VTable g_Demo_vtbl = 
     26 {
     27     Demo_Virtual_Add
     28 };
     29 
     30 static struct VTable g_Derived_vtbl = 
     31 {
     32     Derived_Virtual_Add
     33 };
     34 
     35 Demo* Demo_Create(int i, int j)
     36 {
     37     struct ClassDemo* ret = (struct ClassDemo*)malloc(sizeof(struct ClassDemo)); 
     38 
     39     if( ret != NULL )
     40     {
     41         ret->vptr = &g_Demo_vtbl;   // 4. 关联对象和虚函数表
     42         ret->mi = i;
     43         ret->mj = j;
     44     }
     45     
     46     return ret;
     47 }
     48 
     49 int Demo_GetI(Demo* pThis)
     50 {
     51      struct ClassDemo* obj = (struct ClassDemo*)pThis;    
     52 
     53      return obj->mi;
     54 }
     55 
     56 int Demo_GetJ(Demo* pThis)
     57 {
     58     struct ClassDemo* obj = (struct ClassDemo*)pThis;
     59 
     60     return obj->mj;
     61 }
     62 
     63 // 6. 定义虚函数表中指针所指向的具体函数
     64 static int Demo_Virtual_Add(Demo* pThis, int value)
     65 {
     66     struct ClassDemo* obj = (struct ClassDemo*)pThis;
     67     
     68     return obj->mi + obj->mj + value;
     69 }
     70 
     71 
     72 // 5. 分析具体的虚函数!!!!
     73 int Demo_Add(Demo* pThis, int value)
     74 {
     75 
     76     struct ClassDemo* obj = (struct ClassDemo*)pThis;
     77 
     78     return obj->vptr->pAdd(pThis, value);
     79 }
     80 
     81 void Demo_Free(Demo* pThis)
     82 {
     83     free(pThis);
     84 }
     85 
     86 Derived* Derived_Create(int i, int j, int k)
     87 {
     88     struct ClassDerived* ret = (struct ClassDerived*)malloc(sizeof(struct ClassDerived));
     89     
     90     if( ret != NULL )
     91     {
     92         ret->d.vptr = &g_Derived_vtbl;
     93         ret->d.mi = i;
     94         ret->d.mj = j;
     95         ret->mk = k;
     96     }
     97     
     98     return ret;
     99 }
    100 
    101 int Derived_GetK(Derived* pThis)
    102 {
    103     struct ClassDerived* obj = (struct ClassDerived*)pThis;
    104     
    105     return obj->mk;
    106 }
    107 
    108 static int Derived_Virtual_Add(Demo* pThis, int value)
    109 {
    110     struct ClassDerived* obj = (struct ClassDerived*)pThis; 
    111 
    112     return obj->mk + value;
    113 }
    114 
    115 int Derived_Add(Derived* pThis, int value)
    116 {   
    117     struct ClassDerived* obj = (struct ClassDerived*)pThis;
    118     
    119     return obj->d.vptr->pAdd(pThis, value);
    120 }
     1 #include "stdio.h"
     2 #include "51-2.h"
     3 
     4 void run(Demo* p, int v)
     5 {
     6     int r = Demo_Add(p, v);
     7     
     8     printf("r = %d
    ", r);
     9 }
    10 
    11 int main()
    12 {
    13     Demo* pb = Demo_Create(1, 2);
    14     Derived* pd = Derived_Create(1, 22, 333);
    15     
    16     printf("pb->add(3) = %d
    ", Demo_Add(pb, 3));
    17     printf("pd->add(3) = %d
    ", Derived_Add(pd, 3));
    18     
    19     run(pb, 3);
    20     run(pd, 3);
    21     
    22     Demo_Free(pb);
    23     Demo_Free(pd);
    24     
    25     return 0;
    26 }

    我们定义了虚函数表的结构体类型,并且定义了一个全局的虚函数表类型的变量。

    我们在ClassDemo结构体中塞入一个“虚函数表”指针vtbl。

    我们模拟编译器的行为,在生成对象时,将虚函数的地址写入虚函数表变量中,并将虚函数表变量的地址挂接到ClassDemo结构体中的虚函数表指针上。

    运行结果如下:

    小结:

    补充说明:

      面向对象程序最关键的地方在于必须能够表现三大特性:封装,继承,多态!
    封装指的是类中的敏感数据在外界是不能访问的;继承指的是可以对已经存在的类
    进行代码复用,并使得类之间存在父子关系;多态指的是相同的调用语句可以产生
    不同的调用结果。因此,如果希望用 C 语言完成面向对象的程序,那么肯定的,

    必须实现这三个特性;否则,最多只算得上基于对象的程序(程序中能够看到对象
    的影子,但是不完全具备面向对象的 3 大特性)。

    课程中通过 void* 指针保证具体的结构体成员是不能在外界被访问的,以
    此模拟 C++ 中 private 和 protected。因此,在头文件中定义了如下的语句:
      typedef void Demo;
      typedef void Derived;
    Demo 和 Derived 的本质依旧是 void, 所以,用 Demo* 指针和 Derived* 指针

    指向具体的对象时,无法访问对象中的成员变量,这样就达到了“外界无法访问类
    中私有成员”的封装效果!

      继承的本质是父类成员与子类成员的叠加,所以在用 C 语言写面向对象程
    序的时候,可以直接考虑结构体成员的叠加即可。课程中的实现直接将 struct
    ClassDemo d 作为 struct ClassDerived 的第一个成员,以此表现两个自定义数
    据类型间的继承关系。因为 struct ClassDerived 变量的实际内存分布就是由

    struct ClassDemo 的成员以及 struct ClassDerived 中新定义的成员组成的,这

    样就直接实现了继承的本质,所以说 struct ClassDerived 继承自 struct
    ClassDemo。

      下一步要实现的就是多态了!多态在 C++ 中的实现本质是通过虚函数表完
    成的,而虚函数表是编译器自主产生和维护的数据结构。因此,接下来要解决的问
    题就是如何在 C 语言中自定义虚函数表?课程中认为通过结构体变量模拟 C++
    中的虚函数表是比较理想的一种选择,所以有了下面的代码:

    struct VTable
    {
      int (*pAdd)(void*, int);
    };

      必须要注意的是,课程中由于复制粘贴的缘故,误将 pAdd 指针的类型定义成了
    int (*)(Derived*, int) , 这从 C 语言的角度算不上错误,因为 Derived* 的本质就
    是 void* , 所以编译运行都没有问题。但是,从面向对象的角度,这里可以说是
    一种语义上的错误!因为 pAdd 必须可以指向父类中定义的 Add 函数版本,也可
    以指向子类中定义的 Add 函数版本,所以说用 Derived* 作为第一个参数表示实
    际对象并不合适,应该直接使用 void* 。

      有了类型后就可以定义实际的虚函数表了,在 C 语言中用具有文件作用域
    的全局变量表示实际的虚函数表是最合适的,因此有了下面的代码:

    // 父类对象使用的虚函数表

    static struct VTable g_Demo_vtbl =
    {
      Demo_Virtual_Add
    };
    // 子类对象使用的虚函数表
    static struct VTable g_Derived_vtbl =
    {
      Derived_Virtual_Add
    };

      每个对象中都拥有一个指向虚函数表的指针,而所有父类对象都指向
    g_Demo_vtbl,所以所有子类对象都指向 g_Derived_vtbl。当一切就绪后,实际
    调用虚函数的过程就是通过虚函数表中的对应指针来完成的。

  • 相关阅读:
    日志命令
    QPS、TPS、PV、UV、GMV、IP、RPS
    Tmux实践
    1-2+3-4+5-6....-100 除了88以外其他数字的和 python 实现
    MCNN: 多列卷积神经网络的单图像人群计数
    facenet-pytorch库的简单使用
    SENet笔记
    目标检测 anchor的生成
    matplotlib动图绘制
    感知机算法及其对偶形式
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9581270.html
Copyright © 2011-2022 走看看