• C++内存管理


    关于堆和栈的知识点:

    1. 堆的空间较大,有几个G,其它区都是MB
    2. 堆向上使用空间,栈向下使用空间
    3. 每个程序运行起来都有4G虚拟内存,都是按需申请和释放。
      所以堆一般不可能达4G
    • 例题:
    1. 下面有关c++内存分配堆栈说法错误的是( ):
      A.对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制
      B. 对于栈来讲,生长方向是向下的,也就是向着内存地址减小的方向;对于堆来讲,它的生长方向是向上的,是向着内存地址增加的方向增长
      C.对于堆来讲,频繁的 new/delete 势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题
      D.一般来讲在 32 位系统下,堆内存可以达到4G的空间,但是对于栈来讲,一般都是有一定的空间大小的
    • 分析:
      D:因为32位下,程序使用内存最大4G,不可能都给堆。所以堆达不到4G。

      1. C++中关于堆和栈的说法,哪个是错误的:( )

    A.堆的大小仅受操作系统的限制,栈的大小一般较小
    B.在堆上频繁的调用new/delete容易产生内存碎片,栈没有这个问题
    C.堆和栈都可以静态分配
    D.堆和栈都可以动态分配

    解析:

    1. 栈既可以自动分配也可以手动动态分配,但是栈分配到的不能用free或delete释放。所以D正确,堆栈都可以动态分配。
    2. 堆只能手动动态分配,不能静态分配。
    3. 堆上频繁用new、delete容易产生碎片,栈因为自动分配,所以不存在此问题。

    变量内存区判断:
    4. char* pchar3 = “abcd”;
    指针指向常量字符串的地址,该指针是常量指针,有的编译器会强调加const,因为指向内容不能改变。
    char char2[] = “abcd”
    这个char2[] 数组,char2代表数组首地址,而char2代表的是字母a,
    因为char char2[] = “abcd”左边是字符数组,字符数组都在栈上开辟,而char3是指针,右边是常量,所以两者不一样。
    int
    ptr1 = ()malloc(),这种ptr1是临时变量,在栈,它只是指向了堆区。
    而*ptr1,解引用,变为它存的地址上放置的值

    sizeof:对数字数组,总共几个数字共占的大小
    对字符数组,大小方面,会多一个\0结束符
    strlen:专为字符串和字符数组,结果是 字母个数

    再次注意:
    char char2 = “abcd”
    char*pchar3 = “abcd”
    这两个典型的一个在栈上,一个在堆上,前者因为字符数组是临时变量,后者因为是常量。

    C++动态内存管理
    int* a = new int[5]
    intb = new int(5)
    第一行开辟5个int空间
    第二行带初始化。
    A
    p4 = new A【5】
    A会自动初始化。
    delete和free的区别。
    delete自动调用析构。
    而free之后,还得手动置0,或NULL
    对于自定义类型调用析构,而内置类型不管。

    资源清理:delete

    delete:它自动调用析构。
    【回忆:】析构函数不需要手动调用,编译器自己决定。
    单纯delete解决不了自己开辟的空间归还问题。

    class Test
    {
    private:
        int* a;
    public:
        Test()
        {
             a=new int(0);//需要析构函数来delete,单纯delete Test是无法回收a内存的,直接内存泄漏
         }
    };
    

    free和delete不要混着用。
    new和delete应该匹配:注意括号
    new就用delete
    如果我使用 :A* p = new A[5]
    最后要用:delete[] p;
    C++要new有两个原因:

    1. 定义出来的对象,就一定要初始化。【就是给值】
    2. 抛出异常:面向对象的语言处理错误的方式一般是抛异常,且C++中求报错抛出异常
        面向过程的语言处理错误的方式是:错误码
      C的malloc还有什么问题:
      有时候正常用也可能报错:malloc向堆申请空间,堆虽然很大,但是终究有限,所以还是会有错的可能。
      而C++中new失败了会直接报错,不会返回。
      可以通过捕捉错误,去输出查看是什么类型错误。比起C方便,不用做检查。
      delete和free一般不会失败,如果失败都是释放空间存在越界或释放指针位置不对。
    • 例题:
    1. 下面有关c++内存分配堆栈说法错误的是( )
      A.对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制
      B. 对于栈来讲,生长方向是向下的,也就是向着内存地址减小的方向;对于堆来讲,它的生长方向是向上的,是向着内存地址增加的方向增长
      C.对于堆来讲,频繁的 new/delete 势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题
      D.一般来讲在 32 位系统下,堆内存可以达到4G的空间,但是对于栈来讲,一般都是有一定的空间大小的
      1. 例题:
        c++语言中,类ClassA的构造函数和析构函数的执行次数分别为( )
    ClassA *pclassa=new ClassA[5];
    delete pclassa;
    
    • 解析:
    1. 开辟对象数组大小为几,就调用几次构造函数。
    2. 这是个对象数组,本该用delete[] pclassa;
      而delete会调用一次析构,**且delete一般不是先free,先调用析构函数。**往往会引发程序崩溃。
      new出来的对象数组,如果delete会直接崩掉。
    • 析构的顺序为:
    设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为? ( )
    C c;
    void main()
    {
      A*pa=new A();
      B b;
      static D d;
      delete pa;
    }
    

    ABDC.【因为A手动执行delete,最早调用析构。】
    C和D都在静态区,应按照栈顺序,A在堆上,B在临时栈区,应该最早销毁,最早用析构。
    我本来疑问: new申请的空间,什么时候调用析构。
    属实眼瞎了,人家下面对new出来的pa做了delete,所以delete pa先调用A的析构函数。如下图,C是全局变量,D是静态局部遍历,两者C先定义后是D,销毁按栈特点,先D析构再C析构。因为delete,所以A先做析构。析构和构造顺序相反。
    CBD的构造顺序是:C、D、B,所以最后析构:B、D、C。
    请添加图片描述
    析构顺序完全和构造顺序相反,根据栈特点理解。
    上面先构造了全局对象,再构造了局部静态对象,最后构造普通对象。其中。
    4. 使用 char* p = new char[100]申请一段内存,然后使用delete p释放,有什么问题?( )
    A.会有内存泄露
    B.不会有内存泄露,但不建议用
    C.编译就会报错,必须使用delete []p
    D.编译没问题,运行会直接崩溃

    这个题显然是new和delete不按匹配使用。

    1. A。 char是内置类型,对于内置类型delete和free作用一样,不会造成内存泄漏。
    2. 但是这里毕竟带[],所以建议成对。
    1. 以下代码中,A 的构造函数和析构函数分别执行了几次: ( )
      A*pa=new A[10];
      delete []pa;

    申请空间不调用构造函数,构造函数调用次数是数组的大小。

    例题

    请添加图片描述
    其中,strlen()统计的是字符数量,不统计\0,但是pChar3、char2右值都是常量,它们都以\0结尾。但是char2是个字符数组,且pChar3也是个指针变量,都在栈区存着,sizeof指针,它只求指针大小,所以必然是4,而sizeof字符数组,肯定带着\0

    operator new和operator delete

    operator new、operator delete是new和delete的底层,其中operator new实际通过malloc()来申请空间,但不调用构造函数。operator delete通过free()来释放。它俩是对malloc和free的封装。总之,为了失败时抛出异常错误。使得C++符合面向对象处理错误。
    而new除了申请空间,还会调用构造函数。
    此外,还有 new [],如:Stack* p1 = new class_name[10]; 底层是operator new[],这个封装了operator new。

    手动调用构造函数

    • 前言:new会自动申请和调用类的构造函数(类的私有成员不能直接去操作),而当我们在C++想使用malloc()和构造函数去创建类的对象时该怎么办?
        答:使用operator new。

    malloc/free、new/delete的区别

    请添加图片描述

    什么是内存泄漏?它的危害是什么?

      答:动态申请的内存不需要使用了,也没有释放,存在内存泄漏。但是内存不一定有危害。程序虽然运行过程中,可能会有内存泄漏,但是进程正常结束后,会自动把内存释放还给系统。但是在长期运行的程序,出现内存泄漏,危害很大,系统会越来越慢甚至宕机

    【回忆:乱码原因】
    【随机值字符串编码正好是某个汉字,所以会看见乱码】

    【回忆输出格式】:
    //记住:补充位数和补充值的方法,且用头文件:

    // #include

    // cout << year <<“-” <<setw(2)<

    【面试】
    为什么排序可以提高效率?
    排序后可以减少比较次数。

  • 相关阅读:
    【广州华锐互动】AR技术为气象站远程监控及在线指导维修提供极大便利
    MATLAB矩阵
    链接杂谈 CASPP
    单元测试参数化
    Mysql基础(三)——常用函数与聚合函数
    一个有效的图表图像数据提取框架
    电商api接口进行数据采集获取淘宝/天猫/京东/抖音多平台商品价格
    Java Web笔记 cookie
    JSR303参数校验
    8.12 Day40---Spring&SpringMVC&SpringBoot面试题
  • 原文地址:https://blog.csdn.net/myscratch/article/details/127066768