• C++面向对象核心编程必备知识点之----堆区


    目录

    1.堆区概念

    2.new的基本语法

    2.1相应类型指针变量进行接收

    2.2非对应类型指针变量进行接收(会报错)

    2.3不用指针变量接收(结果?)

     2.4直接解指针输出new内容(结果?)

    3.delete释放new开辟出的内存

    3.1delete在普通变量中的的用法(释放单个空间)

    3.2delete释放数组的用法(释放多个连续空间)

    4.总结和注意事项


    1.堆区概念

    c++中内存有四个区,分别是代码区,全局区,栈区,堆区
    (1)代码区顾名思义就是内存中用来存放代码的区域,具有共享性(减少同时运行两次程序产生复制,造成不必要的资源浪费)和只读性(防止程序运行过程中代码被不经意修改)
    (2)全局区用来存放全局变量、静态变量、常量、字符串常量、counst修饰的全局变量(全局常量)
    (3)栈区用来存放局部变量,函数参数等,栈区数据的注意事项---不要返回局部变量的地址,栈区的数据由编译器管理和释放
    (4)堆区是由开发人员决定的,由我们进行释放,若不释放程序结束后编译器自动释放,可以用new关键字进行开辟内存,用delete释放所开辟的内存

    2.new的基本语法

    2.1相应类型指针变量进行接收

    new关键字用于在堆区中申请内存时,有着一定的语法规则

        //在堆区创建整型数据,就用整型指针接收
        //new返回是该数据类型的指针

    #include

    using namespace std;

    int main()

    {

       int * p = new int((10));

        float * b=new float((35.12f));

        cout<<*p<

        cout<<*b<

        system("pause");

        return 0;

    }

    分别输出int整型指针变量p的内容和float单精度浮点型指针变量b的内容

    结果如下图所示:

    可以正常输出

    2.2非对应类型指针变量进行接收(会报错)

    试着用float类型指针接收开辟的int内存

    1. int main()
    2. {
    3. float * p = new int((10));
    4. int * b = new float((35.12f));
    5. cout << *p << endl;
    6. cout << *b << endl;
    7. system("pause");
    8. return 0;
    9. }

     产生了错误,提示是:int 类型的值不能用于初始化float类型的实体

    从这个报错内容可以看出:01.new返回的同类型的指针,要用同类型指针接收new出来的数据

    2.3不用指针变量接收(结果?)

    如果不对new的内容进行接收,能输出么?

    1. int main()
    2. {
    3. cout << "没有进行接收的new数据输出"<< new int((10)) << endl;
    4. int * p = new int((10));
    5. float * b = new float((35.12f));
    6. cout << *p << endl;
    7. cout << *b << endl;
    8. system("pause");
    9. return 0;
    10. }

    输出结果是一串地址,也间接说明了new关键字开辟出的是地址,既然是地址,就要用指针来接收,上一步验证过了要用同类型的对应指针型变量来接收,并且解指针后输出相应内容

     2.4直接解指针输出new内容(结果?)

    那么直接用*解指针new出的内容呢?

    1. int main()
    2. {
    3. /*int *p = func();
    4. test01();
    5. test02();*/
    6. cout << "没有进行接收的new数据输出"<< new int((10)) << endl;
    7. cout << "解指针new数据输出" << *(new int((10))) << endl;
    8. int * p = new int((10));
    9. float * b = new float((35.12f));
    10. cout << *p << endl;
    11. cout << *b << endl;
    12. system("pause");
    13. return 0;
    14. }

    从输出结果与之后的用指针型变量来接收的结果来看,是一样的,所以,直接输出解指针new的数据可行,但是为啥不常用呢,从个人角度来看,可读性不高且不够美观,所以不常用,习惯性用相应类型指针变量接收再解指针输出,显得更直观些

    3.delete释放new开辟出的内存

    释放内存两种方式:
    new <--> delete
    new [] <--> delete []

    3.1delete在普通变量中的的用法(释放单个空间)

    对于普通变量,比如以下,可以在该变量之前加入delete关键字直接进行释放

    p 是指向动态分配的内存的指针

    1. int main()
    2. {
    3. int *p = new int(10);
    4. cout << *p << endl;
    5. delete p;//释放
    6. cout << *p << endl;//释放了之后能正常输出么?
    7. system("pause");
    8. return 0;
    9. }

    引发了异常,访问权限

    换句直白点的比喻就是说,你手上有100REN,拿去吃了顿饭,把100REN给了饭店老板,相当于这个100REN已经被你释放了,时候再问老板要,人家会给你么,这就产生了100REN的归属权,释放了100REN之后,那个100REN已经不属于你的私人财产范围了,你去染指,那就是超出了你的访问权限,属于非法访问,我们的编译器还是很公道的,不允许违法乱纪

    3.2delete释放数组的用法(释放多个连续空间)

    如果我们按照释放普通变量(释放单个空间)的方法释放数组,会是怎样的?

    1. int main()
    2. {
    3. int * arr = new int[10];//new一个10个元素的数组arr,用int类型指针arr接收
    4. for (int i = 0; i < 10; i++)
    5. {
    6. arr[i] = i + 100;//给每个数组元素赋值,范围是100~109
    7. }
    8. //输出arr[i]
    9. for (int i = 0; i < 10; i++)
    10. {
    11. cout << arr[i] << endl;
    12. }
    13. delete arr;//按照3.1方法释放数组
    14. system("pause");
    15. return 0;
    16. }

    结果如下,虽然可以正常输出,但其实数组并没有被释放

    如果动态分配了一个数组,但是却用delete p的方式释放,没有用[],则编译时没有问题,运行时也一般不会发生错误,但实际上会导致动态分配的数组没有被完全释放。

    牢记,用 new 运算符动态分配的内存空间,一定要用 delete 运算符释放。否则,即便程序运行结束,这部分内存空间仍然不会被操作系统收回,从而成为被白白浪费掉的内存垃圾。这种现象也称为“内存泄露”。

    如果一个程序不停地进行动态内存分配而总是没有释放,那么可用内存就会被该程序大量消耗,即便该程序结束也不能恢复。这就会导致操作系统运行速度变慢,甚至无法再启动新的程序。但是,只要重新启动计算机,这种情况就会消失。

    编程时如果进行了动态内存分配,那么一定要确保其后的每一条执行路径都能释放它。

    另外还要注意,释放一个指针,并不会使该指针的值变为 NULL。

    在释放new数组时,应该告诉编译器释放的是数组类型的arr,一一对应,不仅能达到真正的释放多个连续空间,也能增加代码健壮性

    在arr 前面加[]中括号

    1. int main()
    2. {
    3. int * arr = new int[10];//new一个10个元素的数组arr,用int类型指针arr接收
    4. for (int i = 0; i < 10; i++)
    5. {
    6. arr[i] = i + 100;//给每个数组元素赋值,范围是100~109
    7. }
    8. //输出arr[i]
    9. for (int i = 0; i < 10; i++)
    10. {
    11. cout << arr[i] << endl;
    12. }
    13. //delete arr;
    14. delete[] arr;
    15. arr=NULL;//把释放后的指针置为NULL,这里不用加中括号
    16. system("pause");
    17. return 0;
    18. }

    4.总结和注意事项

    new delete使用时注意以下几点

    1.当传入的形参是数组时,能加const的就加const,防止在函数内部中的意外操作
    2.内存new和delete时,原则:谁申请谁释放
    3.在delete指针后,将指针置为NULL。例如:delete p; p = NULL; 

    这样可以避免出现以下现象:

    1.执行delete语句时,程序直接弹窗,崩溃
    2.执行delete语句时,程序卡死。将delete语句注释掉,又正常了,但发生了内存泄露

  • 相关阅读:
    1. MAC 安装 goland 和 go
    ES6的编程风格(十大编程风格)
    【小程序源码】2022虎年背景全新UI头像框制作带安全检测
    PolarDB-X 源码解读:事务的一生
    PostgreSQL-高级数据类型
    2024Mac系统热门游戏排行榜 Mac支持的网络游戏有哪些?mac能玩哪些大型网游 苹果电脑Mac游戏资源推荐 Mac玩Windows游戏
    【毕业季】走一步看一步?一个自动化er对大学四年的思考
    Python期末复习题:组合数据类型
    SQL故障和排查解决浅析
    银行家算法(避免死锁的算法)
  • 原文地址:https://blog.csdn.net/qq_58619891/article/details/127658251