• 堆,栈,内存管理, 拓展补充-Geekband





    8, 堆,栈,内存管理



    栈:  local objects 在离开作用域之后就会被消除. 
    堆: new MyClass 一直会存在
    静态对象: static local object    作用域在当前函数,其生命在整个程序结束后才会结束. 
    全局对象: Global object           作用域在全局.


    new函数的内部实现
    e.g Complex * pc = new Complex(1,2);

    分解: 
    1, void* mem = operator new(sizeof(Complex));    -分配内存 使用molloc实现
    2, pc = static_cast<Complex*>(mem);     - 转型
    3, pc->Complex::Complex(1,2);       - 构造函数

    delete函数的内部实现
    e.g delete pc;

    分解:
    1, String::~String(pc);        -析构函数
    2, operator delete(pc);      -释放内存 内部使用free实现


    动态分配得到的内存块:
    debug:
    头尾: cookie (小甜饼干) 4x2
    复数 : 8b
    最终加起来得到的内存块为 52b
    但是在vc下, 每个内存块分配都是16的倍数.
    所以,这里需要填补(pad) 得到64


    release: 

    正好是16的倍数.

    cookie说明:
    cookie的作用是记录整块分配内存的大小.
    因为都是16的倍数,所以最后有4个为是空的. 所以最后那位我们可以使用.
    当最后一位是1时,说明是系统分配给我们的. 
    如果我们要把内存还给系统,那么最后一位就会变成0.


    vc中动态分配数组
    debug:


    8*3是数据
    32+4是调试相关字符
    4*2是cookie
    最后的4是数组的大小 保存数据'3'
    最后80是要向16对齐.


    release:

    内存泄露不是我们所以为的内存泄露:

    array new 一定要搭配array delete 

    e.g String* p = new String[3];

    delete[] p;    -唤起3次析构函数 
    delete p;      -唤起1次析构函数

    无论上面哪种方式,都会删除cookie中间的那块内存. 
    所以问题是出现在里面保存对象是否调用了析构函数. 
    各自的析构就会负责把自己动态分配的内存删除掉. 
    所以, 如果数组中的对象不含有指针.所以只用delete也是可以的,也不会造成内存泄露. 

    疑问:
    为什么系统不直接根据 那个'3'字节来将全部对象调用析构? 而是非得要求用户调用delete[]




    9,复习String类的实现

    strlen取得的长度不含''

    在拷贝赋值中, 返回类型尽量使用 MyClass& MyClass::operator=(const MyClass&  m2) !
    而不是void MyClass::operator=(const MyClass&  m2)  
    使用void在一般情况: m1 = m2 这样的一个赋值上是没有问题的. 但, 当一连串赋值的时候就必须使用返回引用了! ! !  ! 
    这就贯彻了之前说过的 "调用者无需知道函数是使用什么方式传递出去的"


    一定要注意 取地址符号&和引用符号&的区分! 





    10, 拓展补充

    static 关键字

    静态数据 
    MyClass{
    static int count;    //-  无论创造多少个对象,  这个变量只有一份. 
    static void Set_Count(int cc) { count  = cc;}
    };
    注意: 静态变量 一定要在类外进行定义(我们俗称初始化);
    int MyClass::count = 10;    //格式一定要注意! 前面的类型一定要写!后面要不要赋值不是必要.


    静态函数调用方式: 
    1,通过对象来调用:
    MyClass  my;
    my.Set_Count(2);
    2,通过类名来调用:
    MyClass::Set_Count(3);


    单例模式:
    把构造函数放在private区内. 这样就不能通过普通模式构建对象, 此时再声明一个静态成员函数,用于返回一个实例化对象. 





    8, 堆,栈,内存管理



    栈:  local objects 在离开作用域之后就会被消除. 
    堆: new MyClass 一直会存在
    静态对象: static local object    作用域在当前函数,其生命在整个程序结束后才会结束. 
    全局对象: Global object           作用域在全局.


    new函数的内部实现
    e.g Complex * pc = new Complex(1,2);

    分解: 
    1, void* mem = operator new(sizeof(Complex));    -分配内存 使用molloc实现
    2, pc = static_cast<Complex*>(mem);     - 转型
    3, pc->Complex::Complex(1,2);       - 构造函数

    delete函数的内部实现
    e.g delete pc;

    分解:
    1, String::~String(pc);        -析构函数
    2, operator delete(pc);      -释放内存 内部使用free实现


    动态分配得到的内存块:
    debug:

    头尾: cookie (小甜饼干) 4x2
    复数 : 8b
    最终加起来得到的内存块为 52b
    但是在vc下, 每个内存块分配都是16的倍数.
    所以,这里需要填补(pad) 得到64


    release: 

    正好是16的倍数.

    cookie说明:
    cookie的作用是记录整块分配内存的大小.
    因为都是16的倍数,所以最后有4个为是空的. 所以最后那位我们可以使用.
    当最后一位是1时,说明是系统分配给我们的. 
    如果我们要把内存还给系统,那么最后一位就会变成0.


    vc中动态分配数组
    debug:

    8*3是数据
    32+4是调试相关字符
    4*2是cookie
    最后的4是数组的大小 保存数据'3'
    最后80是要向16对齐.


    release:


    内存泄露不是我们所以为的内存泄露:

    array new 一定要搭配array delete 

    e.g String* p = new String[3];

    delete[] p;    -唤起3次析构函数 
    delete p;      -唤起1次析构函数

    无论上面哪种方式,都会删除cookie中间的那块内存. 
    所以问题是出现在里面保存对象是否调用了析构函数. 
    各自的析构就会负责把自己动态分配的内存删除掉. 
    所以, 如果数组中的对象不含有指针.所以只用delete也是可以的,也不会造成内存泄露. 

    疑问:
    为什么系统不直接根据 那个'3'字节来将全部对象调用析构? 而是非得要求用户调用delete[]




    9,复习String类的实现

    strlen取得的长度不含''

    在拷贝赋值中, 返回类型尽量使用 MyClass& MyClass::operator=(const MyClass&  m2) !
    而不是void MyClass::operator=(const MyClass&  m2)  
    使用void在一般情况: m1 = m2 这样的一个赋值上是没有问题的. 但, 当一连串赋值的时候就必须使用返回引用了! ! !  ! 
    这就贯彻了之前说过的 "调用者无需知道函数是使用什么方式传递出去的"


    一定要注意 取地址符号&和引用符号&的区分! 





    10, 拓展补充

    static 关键字

    静态数据 
    MyClass{
    static int count;    //-  无论创造多少个对象,  这个变量只有一份. 
    static void Set_Count(int cc) { count  = cc;}
    };
    注意: 静态变量 一定要在类外进行定义(我们俗称初始化);
    int MyClass::count = 10;    //格式一定要注意! 前面的类型一定要写!后面要不要赋值不是必要.


    静态函数调用方式: 
    1,通过对象来调用:
    MyClass  my;
    my.Set_Count(2);
    2,通过类名来调用:
    MyClass::Set_Count(3);


    单例模式:
    把构造函数放在private区内. 这样就不能通过普通模式构建对象, 此时再声明一个静态成员函数,用于返回一个实例化对象. 




  • 相关阅读:
    async和await是如何实现异步编程?
    HD-ACM算法专攻系列(23)——Crixalis's Equipment
    HD-ACM算法专攻系列(22)——Max Sum
    HD-ACM算法专攻系列(21)——Wooden Sticks
    HD-ACM算法专攻系列(20)——七夕节
    HD-ACM算法专攻系列(19)——Leftmost Digit
    搭建Prometheus平台,你必须考虑的6个因素
    实用教程丨使用K3s和MySQL运行Rancher 2.4
    Kubernetes Ingress简单入门
    一文讲透Cluster API的前世、今生与未来
  • 原文地址:https://www.cnblogs.com/skyhuangdan/p/5486767.html
Copyright © 2020-2023  润新知