• C++内存管理:其五、指针类型转换与嵌入式指针


    一、内存池的缺陷

    作者在上一版本里面介绍了链表实现内存池,其中有一个小缺陷:虽然较少了cookie的内存损耗,但是加入了一个额外的指针,仍然需要占用内存。我们仔细看内存池的设计思想,可以发现一个关键点:
    对于一个内存切片,如果放置在freeList中,才会使用指针。如果被用于构造对象,则这个指针毫无用处。
    于是可以想到,可以将一块内存区域,即作为指针使用,又用于构造对象。
    方案一:共同体,这个东西过于古早了,不过多解释。
    方案二:嵌入式指针。

    二、指针类型转换

    想要把嵌入式指针讲清楚,先要把指针类型转换讲清楚。
    在C++里面,64位操作系统下,所有指针都是八字节,表示一个地址。那么为什么指定指针的类型呢?编译器根据指针定位到这个内存地址之后,根据指针类型去解析这个数据。举个例子,假如是一个int类型的指针,定位到这个地址之后,扫描后面的四个字节,去解析这32位二进制代表的int数字是多少。
    说一个看起来违背常识的事情,指针之间转换,基本是不被编译器报错的!!但是有可能解析出来一大堆稀奇古怪的东西,所以最好不要这样做。也就是说,给编译器一个地址和数据类型,编译器就可以解析,至于解析出来的是什么东西,由程序员负责
    看代码:

    #include 
    using namespace std;
    
    class Test
    {
    public:
        int m_i;
        int m_j;
    };
    
    class A{
    public:
        int a;
    };
    
    int main()
    {
        Test t ;
        t.m_i=1000;
        t.m_j=2;
        A *a=(A*)&t;
        cout<<a->a<<endl;
        cout<<t.m_j;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    输出结果:

    1000
    2
    
    • 1
    • 2

    说明一点:
    (1)Test类的字节数大于A类,将Test指针强转为A类型指针后,相当于使用前面的地址,后面的地址也不会被抛弃,只是当前不用。
    (2)不考虑cookie的情况下,a->a相当于解析t的前四位字节,恰好前四位也是int类型,就可以解析出来t.m_i对应的值。

    这是大结构体的指针转换为指向小结构体的指针,如果反之会怎么样?
    看代码:

    #include 
    using namespace std;
    
    class Test
    {
    public:
        int m_i;
        int m_j;
    };
    
    class A{
    public:
        int a;
    };
    
    int main()
    {
        A a;
        a.a =666;
        Test * t = (Test *)&a;
        cout<<t->m_i<<endl;
        cout<<t->m_j;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    运行结果:

    666
    -2120222908
    
    • 1
    • 2

    第一个成员变量可以正常输出,但是对于第二个成员变量拿到了垃圾数值。但是编译器没有限制这个行为,编译正常,也可以正常退出。

    三、嵌入式指针

    嵌入式指针的核心思想:在这个结构体不用于存储数据的时候,将前八个字节强转为指针,将多个结构体按照链表进行管理。看代码:

    #include 
    using namespace std;
    
    class Test
    {
    public:
        int m_i;
        int m_j;
        struct Obj  // 相当于在类中定义了一个结构体
        {
            struct Obj* pNext;   // 这个pNext是一个嵌入式指针,64位系统下占了八个字节
        };
    };
    
    int main()
    {
        Test test1;
        cout << "Test size is "<<sizeof(test1) << endl;
        Test::Obj* pTemp;
        pTemp = (Test::Obj*)&test1;   //类型强转,pTemp实际上指向test1的前两八个字节
    
        Test test2;
        pTemp->pNext = (Test::Obj*)&test2;  //操作以此类推
    
        Test test3;
        ((Test::Obj*)&test2)->pNext = (Test::Obj*)&test3;
    
        ((Test::Obj*)&test3)->pNext = nullptr;
    
    }
    
    • 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

    注意要点:
    (1)struct Obj 定义在Test类里面,但是只是定义,没有创建对象,不会占用空间,所以Test对象的大小是八个字节。

    (2)嵌入式指针的一个重要原则:使用嵌入式指针的类的size必须大于指针大小,不然强转会有问题。

    (3)示例代码来自侯捷老师的课程,实际上struct Obj 大可不必定义在Test类里面,定义在外面也是可行的。但是考虑到这个struct Obj 只被Test使用,定义在Test中比较规格。

  • 相关阅读:
    Keepalived+LVS构建高可用集群
    使用XShell、XFTP连接虚拟机或者服务器教程
    Qt全球峰会2023中国站 参会概要
    矢量图形编辑软件Boxy SVG mac中文版软件特点
    windows mysql安装卸载,多版本mysql方案
    c++day5
    LeetCode每日一题:1993. 树上的操作(2023.9.23 C++)
    计算机毕业设计(附源码)python裕民镇养老院信息管理系统
    设计模式:干掉if else的几种方法
    使用boost封装一个websocketserver类
  • 原文地址:https://blog.csdn.net/jiexianxiaogege/article/details/133877741