zoukankan      html  css  js  c++  java
  • 37 C++ 对象模型

    1 class 与 struct

    • class 是一种特殊的 struct

      • 在内存中,class 依旧可以看作变量的集合
      • classstruct 遵循相同的内存对齐规则
      • class 中的成员函数(是函数,只可能存放在代码段中)成员变量(是数据,存放于三个数据区中:栈,堆,全局数据区)是分开存放的
        • 每个对象有独立的成员变量
        • 所有对象共享类中的成员函数
    • 问题:sizeof(A) = ?,sizeof(B) = ?

      • sizeof(A) = 20
      • sizeof(B) = 20
      class A 
      {
          int i;
          int j;
          char c;
          double d;
      };
      
      struct B
      {
          int i;
          int j;
          char c;
          double d;
      };
      
    • 示例1:对象内存布局

      • Demo

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class A
        {
            int i;
            int j;
            char c;
            double d;
        public:
            void print()
            {
                cout << "i = " << i << ", "
                     << "j = " << j << ", "
                     << "c = " << c << ", "
                     << "d = " << d << endl;
            }
        };
        
        struct B
        {
            int i;
            int j;
            char c;
            double d;
        };
        
        int main()
        {
            A a;
            
            cout << "sizeof(A) = " << sizeof(A) << endl;    // 20 bytes
            cout << "sizeof(a) = " << sizeof(a) << endl;    // 20 bytes
            cout << "sizeof(B) = " << sizeof(B) << endl;    // 20 bytes
            
            a.print();
            
            //强制类型转换:重新解释这段内存
            B* p = reinterpret_cast<B*>(&a);
            
            p->i = 1;
            p->j = 2;
            p->c = 'c';
            p->d = 3;
            
            a.print();
            
            p->i = 100;
            p->j = 200;
            p->c = 'C';
            p->d = 3.14;
            
            a.print();
            
            return 0;
        }
        
      • 编译运行

        sizeof(A) = 20
        sizeof(a) = 20
        sizeof(B) = 20
        i = 134515232, j = -1075608584, c = ?, d = 4.85991e-270
        i = 1, j = 2, c = c, d = 3
        i = 100, j = 200, c = C, d = 3.14
        

    2 C++ 对象模型分析

    • 运行时的对象退化为结构体的形式

      • 所有成员变量在内存中依次分布
      • 成员变量间可能存在内存空隙
      • 可以通过内存地址直接访问成员变量
      • 访问权限关键字在运行时失效
    • 类的对象调用成员函数原理

      • 类的成员函数位于代码段
      • 调用成员函数时对象地址作为参数隐式传递
      • 成员函数通过对象地址访问成员变量
      • C++ 语法规则隐藏了对象地址的传递过程
    • 示例2:对象本质分析

      • Demo1:C++ 类对象调用成员函数

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class Demo
        {
            int mi;
            int mj;
        public:
            Demo(int i, int j)
            {
                mi = i;
                mj = j;
            }
            
            int getI()
            {
                return mi;
            }
            
            int getJ()
            {
                return mj;
            }
            
            int add(int value)
            {
                return mi + mj + value;
            }
        };
        
        int main()
        {
            Demo d(1, 2);
            
            cout << "sizeof(d) = " << sizeof(d) << endl;  //8
            cout << "d.getI() = " << d.getI() << endl;
            cout << "d.getJ() = " << d.getJ() << endl;
            cout << "d.add(3) = " << d.add(3) << endl;
            
            return 0;
        }
        
      • Demo2:C 语言实现上述过程

        //test.c
        #include <stdio.h>
        #include "object.h"
        
        int main()
        {
            Demo* d = Demo_Create(1, 2);             // Demo* d = new Demo(1, 2);
            
            printf("d.mi = %d
        ", Demo_GetI(d));     // d->getI();
            printf("d.mj = %d
        ", Demo_GetJ(d));     // d->getJ();
            printf("Add(3) = %d
        ", Demo_Add(d, 3));    // d->add(3);
            
            // d->mi = 100;  //error
            
            Demo_Free(d);
            
            return 0;
        }
        
        
        //object.h
        #ifndef _OBJECT_H_
        #define _OBJECT_H_
        
        typedef void Demo;
        
        Demo* Demo_Create(int i, int j);
        int Demo_GetI(Demo* pThis);
        int Demo_GetJ(Demo* pThis);
        int Demo_Add(Demo* pThis, int value);
        void Demo_Free(Demo* pThis);
        
        #endif
        
        
        //object.c
        #include "object.h"
        #include "malloc.h"
        
        struct ClassDemo
        {
            int mi;
            int mj;
        };
        
        Demo* Demo_Create(int i, int j)
        {
            struct ClassDemo* ret = (struct ClassDemo*)malloc(sizeof(struct ClassDemo));
            
            if( ret != NULL )
            {
                ret->mi = i;
                ret->mj = j;
            }
            
            return ret;
        }
        
        int Demo_GetI(Demo* pThis)
        {
             struct ClassDemo* obj = (struct ClassDemo*)pThis;
             
             return obj->mi;
        }
        
        int Demo_GetJ(Demo* pThis)
        {
            struct ClassDemo* obj = (struct ClassDemo*)pThis;
             
            return obj->mj;
        }
        
        int Demo_Add(Demo* pThis, int value)
        {
            struct ClassDemo* obj = (struct ClassDemo*)pThis;
             
            return obj->mi + obj->mj + value;
        }
        
        void Demo_Free(Demo* pThis)
        {
            free(pThis);
        }
        
      • 编译运行

        d.mi = 1
        d.mi = 2
        Add(3) = 6
        

    3 继承对象模型

    • 在 C++ 编译器的内部,类可以理解为结构体

    • 子类是由父类成员叠加子类新成员得到的

      class Derived : public Demo
      {
          int  mk;
      }
      
    • 示例3:继承对象模型

      • Demo

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class Demo
        {
        protected:
            int mi;
            int mj;
        public:
            void print()
            {
                cout << "mi = " << mi << ", "
                     << "mj = " << mj << endl;
            }
        };
        
        class Derived : public Demo
        {
            int mk;
        public:
            Derived(int i, int j, int k)
            {
                mi = i;
                mj = j;
                mk = k;
            }
            
            void print()
            {
                cout << "mi = " << mi << ", "
                     << "mj = " << mj << ", "
                     << "mk = " << mk << endl;
            }
        };
        
        struct Test
        {
            int mi;
            int mj;
            int mk;
        };
        
        int main()
        {
            cout << "sizeof(Demo) = " << sizeof(Demo) << endl;         
            cout << "sizeof(Derived) = " << sizeof(Derived) << endl;  
            
            Derived d(1, 2, 3);
            Test* p = reinterpret_cast<Test*>(&d);
            
            cout << "Before changing ..." << endl;
            
            d.print();
            
            p->mi = 10;
            p->mj = 20;
            p->mk = 30;
            
            cout << "After changing ..." << endl;
            
            d.print();
            
            return 0;
        }
        
    • 编译运行:说明结构体 Test 和类 Derived 的内存分布相同

        sizeof(Demo) = 8
        sizeof(Derived) = 12
        Before changing ...
        mi = 1, mj = 2, mk = 3
        After changing ...
        mi = 10, mj = 20, mk = 30
      

    4 多态对象模型

    • 多态的概念:相同的行为方式,不同的行为结果。由具体的编程语言实现,在 C++ 中由虚函数实现

    • C++ 多态的实现原理

      • 当类中声明虚函数时,编译器会在类中生成一个虚函数表(VTABLE)
      • 虚函数表是一个存储成员(虚)函数地址的数据结构
      • 虚函数表是由编译器自动生成维护的
      • virtual 成员函数会被编译器放入虚函数表中
      • 存在虚函数时,每个对象中都有一个指向虚函数表的指针:虚函数表指针(VPTR)
    • 示例4:多态对象模型

      • Demo

        #include <iostream>
        #include <string>
        
        using namespace std;
        
        class Demo
        {
        protected:
            int mi;
            int mj;
        public:
            virtual void print()
            {
                cout << "mi = " << mi << ", "
                     << "mj = " << mj << endl;
            }
        };
        
        class Derived : public Demo
        {
            int mk;
        public:
            Derived(int i, int j, int k)
            {
                mi = i;
                mj = j;
                mk = k;
            }
            
            void print()
            {
                cout << "mi = " << mi << ", "
                     << "mj = " << mj << ", "
                     << "mk = " << mk << endl;
            }
        };
        
        struct Test
        {
            void* p;  //模拟虚函数表指针,放在最开始的四个字节处
            int mi;
            int mj;
            int mk;
        };
        
        int main()
        {
            cout << "sizeof(Demo) = " << sizeof(Demo) << endl;         
            cout << "sizeof(Derived) = " << sizeof(Derived) << endl;  
            
            Derived d(1, 2, 3);
            Test* p = reinterpret_cast<Test*>(&d);
            
            cout << "Before changing ..." << endl;
            
            d.print();
            
            p->mi = 10;
            p->mj = 20;
            p->mk = 30;
            
            cout << "After changing ..." << endl;
            
            d.print();
            
            return 0;
        }
        
      • 编译运行

        sizeof(Demo) = 12
        sizeof(Derived) = 16
        Before changing ...
        mi = 1, mj = 2, mk = 3
        After changing ...
        mi = 10, mj = 20, mk = 30
        
    • 示例:多态本质分析

      • Demo

        //test.c
        #include "stdio.h"
        #include "51-2.h"
        
        void run(Demo* p, int v)
        {
            int r = Demo_Add(p, v);
            
            printf("r = %d
        ", r);
        }
        
        int main()
        {
            Demo* pb = Demo_Create(1, 2);
            Derived* pd = Derived_Create(1, 22, 333);
            
            printf("pb->add(3) = %d
        ", Demo_Add(pb, 3));
            printf("pd->add(3) = %d
        ", Derived_Add(pd, 3));
            
            run(pb, 3);
            run(pd, 3);
            
            Demo_Free(pb);
            Demo_Free(pd);
            
            return 0;
        }
        
        
        //
        #ifndef _51_2_H_
        #define _51_2_H_
        
        typedef void Demo;
        typedef void Derived;
        
        Demo* Demo_Create(int i, int j);
        int Demo_GetI(Demo* pThis);
        int Demo_GetJ(Demo* pThis);
        int Demo_Add(Demo* pThis, int value);
        void Demo_Free(Demo* pThis);
        
        Derived* Derived_Create(int i, int j, int k);
        int Derived_GetK(Derived* pThis);
        int Derived_Add(Derived* pThis, int value);
        
        #endif
        
        
        //
        #include "51-2.h"
        #include "malloc.h"
        
        static int Demo_Virtual_Add(Demo* pThis, int value);
        static int Derived_Virtual_Add(Demo* pThis, int value);
        
        struct VTable     // 2. 定义虚函数表数据结构
        {
            int (*pAdd)(void*, int);   // 3. 虚函数表里面存储什么???
        };
        
        struct ClassDemo
        {
            struct VTable* vptr;     // 1. 定义虚函数表指针  ==》 虚函数表指针类型???
            int mi;
            int mj;
        };
        
        struct ClassDerived
        {
            struct ClassDemo d;
            int mk;
        };
        
        static struct VTable g_Demo_vtbl = 
        {
            Demo_Virtual_Add
        };
        
        static struct VTable g_Derived_vtbl = 
        {
            Derived_Virtual_Add
        };
        
        Demo* Demo_Create(int i, int j)
        {
            struct ClassDemo* ret = (struct ClassDemo*)malloc(sizeof(struct ClassDemo)); 
        
            if( ret != NULL )
            {
                ret->vptr = &g_Demo_vtbl;   // 4. 关联对象和虚函数表
                ret->mi = i;
                ret->mj = j;
            }
            
            return ret;
        }
        
        int Demo_GetI(Demo* pThis)
        {
             struct ClassDemo* obj = (struct ClassDemo*)pThis;    
        
             return obj->mi;
        }
        
        int Demo_GetJ(Demo* pThis)
        {
            struct ClassDemo* obj = (struct ClassDemo*)pThis;
        
            return obj->mj;
        }
        
        // 6. 定义虚函数表中指针所指向的具体函数
        static int Demo_Virtual_Add(Demo* pThis, int value)
        {
            struct ClassDemo* obj = (struct ClassDemo*)pThis;
            
            return obj->mi + obj->mj + value;
        }
        
        
        // 5. 分析具体的虚函数!!!!
        int Demo_Add(Demo* pThis, int value)
        {
        
            struct ClassDemo* obj = (struct ClassDemo*)pThis;
        
            return obj->vptr->pAdd(pThis, value);
        }
        
        void Demo_Free(Demo* pThis)
        {
            free(pThis);
        }
        
        Derived* Derived_Create(int i, int j, int k)
        {
            struct ClassDerived* ret = (struct ClassDerived*)malloc(sizeof(struct ClassDerived));
            
            if( ret != NULL )
            {
                ret->d.vptr = &g_Derived_vtbl;
                ret->d.mi = i;
                ret->d.mj = j;
                ret->mk = k;
            }
            
            return ret;
        }
        
        int Derived_GetK(Derived* pThis)
        {
            struct ClassDerived* obj = (struct ClassDerived*)pThis;
            
            return obj->mk;
        }
        
        static int Derived_Virtual_Add(Demo* pThis, int value)
        {
            struct ClassDerived* obj = (struct ClassDerived*)pThis; 
        
            return obj->mk + value;
        }
        
        int Derived_Add(Derived* pThis, int value)
        {   
            struct ClassDerived* obj = (struct ClassDerived*)pThis;
            
            return obj->d.vptr->pAdd(pThis, value);
        }
        
      • 编译运行

  • 相关阅读:
    Android Studio打开非本机项目比较慢的问题。
    Servlet实现重定向的两种方式
    Servlet实现定时刷新到另外一个页面response.setHeader("refresh", "3;url=/...")
    Servlet实现自动刷新功能
    自己实现一个验证码功能
    使用Servlet实现图片下载
    数据库备份的几种方法
    servlet实现的三种方式对比(servlet 和GenericServlet和HttpServlet)
    java中this的用法如:this.name=name
    步骤一:下载jdk并安装和配置java环境变量
  • 原文地址:https://www.cnblogs.com/bky-hbq/p/13903923.html
Copyright © 2011-2022 走看看