• 50-51 - C++对象模型分析


    ---- 整理自狄泰软件唐佐林老师课程

    1. 回归本质

    1.1 class 是一种特殊的 struct

    • 在内存中 class 依旧可以看作 变量的集合
    • class 与 struct 遵循相同的 内存对齐 规则
    • class 中的成员函数与成员变量是 分开存放
      • 每个对象有独立的成员变量
      • 所有对象共享类中的成员函数

    在这里插入图片描述

    1.2 值得思考的问题

    在这里插入图片描述

    1.3 编程实验:对象内存布局初探

    #include 
    #include 
    
    using namespace std;
    
    class A
    {
        int i; // 4
        int j; // 4
        char c; // 1 --> 8
    	double d; // 8
    public:
        void print() {
            cout << "i = " << i << ", " << "j = " << j << ", "
                 << "c = " << c << ", " << "d = " << d << endl;
        }
    };
    
    struct B
    {
        int i; // 4
        int j; // 4
        char c; // 1 --> 8
    	double d; // 8
    };
    
    int main()
    {
        A a;
        cout << "sizeof(A) = " << sizeof(A) << endl;
        cout << "sizeof(a) = " << sizeof(a) << endl;
        cout << "sizeof(B) = " << sizeof(B) << endl;
        a.print();
        
        B* p = reinterpret_cast<B*>(&a); // 强制类型转换,重解释 A 这一段内存
    									 // struct 解释 class
        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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    在这里插入图片描述

    2. C++ 对象模型分析

    2.1 对象本质分析

    • 成员变量:
      • 运行时的对象退化为 结构体 的形式
      • 所有成员变量在内存中 依次排布
      • 成员变量间 可能存在内存空隙
      • 可以通过对象的内存地址直接访问成员变量
      • 访问权限在编译时有效,一旦编译通过,访问权限 在运行时失效,运行时就可以通过指针修改成员变量的值
    • 成员函数:
      • 类中的成员函数位于代码段中
      • 调用成员函数时 对象地址 作为参数 隐式传递this 指针)
      • 成员函数通过 对象地址 访问成员变量
      • C++ 语法规则 隐藏了 对象地址的传递过程

    2.1.1 编程实验:对象本质分析

    #include 
    #include 
    
    using namespace std;
    
    class Demo
    {
        int mi;
        int mj;
    public:
        Demo(int i, int j) {
            mi = i;
            mj = j;
        }
        int getI() {
    		cout << "this: " << this << endl;
            return mi;
        }
        int getJ() {
    		cout << "this: " << this << endl;
            return mj;
        }
        int add(int value) {
    		cout << "this: " << this << endl;
            return mi + mj + value;
        }
    };
    
    int main()
    {
        Demo d(1, 2);
        
        cout << "sizeof(d) = " << sizeof(d) << endl;
        cout << "&d = " << &d << endl;
        cout << "d.getI() = " << d.getI() << endl; // d 对象的地址被传入到成员函数内部
        cout << "d.getJ() = " << d.getJ() << endl;
        cout << "d.add(3) = " << d.add(3) << endl;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    在这里插入图片描述

    2.1.2 用 C 语言模拟

    #ifndef __DEMO_H_
    #define __DEMO_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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    #include 
    #include "demo.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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    #include 
    #include "demo.h"
    
    int main()
    {
    	Demo* d = Demo_create(1, 2); // 模拟 Demo* d = new Demo(1,2);
    	printf("d.mi = %d\n", Demo_GetI(d)); // 模拟 d->GetI();
    	printf("d.mj = %d\n", Demo_GetJ(d)); // 模拟 d->GetJ();
    	printf("Add(3) = %d\n", Demo_Add(d, 3)); // 模拟 d->Add(3);
    	
    	// d->mi = 100; // 编译报错,模拟 C++ 外界不能访问私有成员
    	
    	Demo_Free(d); // 模拟 C++ 中的析构
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    2.2 继承对象模型

    • 在 C++ 编译器的内部,类可以理解为结构体
    • 子类是由 父类成员叠加子类新成员 得到的(父类在前,子类在后

    在这里插入图片描述

    • 编程实验:继承对象模型初探
    #include 
    #include 
    
    using namespace std;
    
    class Demo
    {
    public:
        int mi;
        int mj;
    };
    
    class Derived : public Demo
    {
    public:
    	int mk;
    };
    
    
    int main()
    {
    	Derived d;
    	
    	cout << "&d.mi = " << &d.mi << endl;
    	cout << "&d.mj = " << &d.mj << endl;
    	cout << "&d.mk = " << &d.mk << endl;
    	
        cout << "sizeof(Demo)    = " << sizeof(Demo) << endl;
        cout << "sizeof(Derived) = " << sizeof(Derived) << endl;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在这里插入图片描述

    #include 
    #include 
    
    using namespace std;
    
    class Demo
    {
    protected:
        int mi;
        int mj;
    };
    
    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);
    	d.print();
    	
    	cout << "Before changing ..." << endl;
    
    	p->mi = 10;
    	p->mj = 20;
    	p->mk = 30;
    	
    	cout << "After changing ..." << endl;
    	
    	d.print();
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    在这里插入图片描述

    2.3 多态对象模型

    • C++ 多态的实现原理
      • 当类中声明虚函数时,编译器会在类中生成一个 虚函数表
      • 虚函数表是一个 存储 成员函数地址 的数据结构
      • 虚函数表是由编译器自动生成与维护的
      • virtual 成员函数 会被编译器放入虚函数表中
      • 存在虚函数时,每个对象 中都有一个 指向虚函数表的指针

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    • 编程实验:多态本质分析
    #include 
    #include 
    
    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;
    	}
    };
    
    int main()
    {
    	cout << "sizeof(void*)   = " << sizeof(void*) << endl;
        cout << "sizeof(Demo)    = " << sizeof(Demo) << endl;
        cout << "sizeof(Derived) = " << sizeof(Derived) << endl;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    在这里插入图片描述

    • 指向虚函数表的指针放在 最开始的 4 个字节处(32 位环境),可通过如下实验验证:
    #include 
    #include 
    
    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(void*)   = " << sizeof(void*) << endl;
        cout << "sizeof(Demo)    = " << sizeof(Demo) << endl;
        cout << "sizeof(Derived) = " << sizeof(Derived) << endl;
    	
    	Derived d(1, 2, 3);
    	Test* p = reinterpret_cast<Test*>(&d);
    	d.print();
    	
    	cout << "Before changing ..." << endl;
    
    	p->mi = 10;
    	p->mj = 20;
    	p->mk = 30;
    	
    	cout << "After changing ..." << endl;
    	
    	d.print();
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    在这里插入图片描述

    • 用 C 语言实现多态:
    #ifndef __DEMO_H_
    #define __DEMO_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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    #include 
    #include "demo.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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    #include 
    #include "demo.h"
    
    void run(Demo* p, int v)
    {
        int r = Demo_Add(p, v);
        printf("r = %d\n", r);
    }
    
    int main()
    {
        Demo* pb = Demo_Create(1, 2);
        Derived* pd = Derived_Create(10, 20, 30);
        
        printf("pb->add(3) = %d\n", Demo_Add(pb, 3));
        printf("pd->add(30) = %d\n", Derived_Add(pd, 30));
        
        run(pb, 3);
        run(pd, 30);
        
        Demo_Free(pb);
        Demo_Free(pd);
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述

    3. 小结

    • C++ 中的类对象在内存布局上与结构体相同
    • 成员变量和成员函数在内存中分开存放
    • 访问权限关键字在运行时失效
    • 调用成员函数时对象地址作为参数隐式传递
    • 继承的 本质 是父子间成员变量的 叠加
    • C++ 中的多态是通过虚函数表实现的
    • 虚函数表是由编译器自动生成与维护的
    • 虚函数的调用效率低于普通函数
  • 相关阅读:
    C++ 函数模板
    开发指南031-安装ssl证书
    ASP.NET Core - 依赖注入(一)
    随着 ChatGPT 凭借 GPT-4V(ision) 获得关注,多模态 AI 不断发展
    gcc编程4步编译、调试c程序实操详解(Linux系统编程)
    记一次 ClickHouse 性能测试
    contenteditable格式化html文本转svg
    vue内置组件:keep-alive
    C语言笔记第15篇:文件操作
    Ubuntu18.04更改镜像源(网易,阿里,清华,中科大,浙大)
  • 原文地址:https://blog.csdn.net/weixin_36098975/article/details/128037674