C++基础——new和delete动态开辟

2023-11-18

     目录

         前言:

一..new关键字

格式2: 动态开辟多个数据的堆区空间——数组

总结:

二.自定义类型的开辟

1.区别:

 2.匹配错误讲解:

        错误示范例1:

delete[] 的执行原理:

错误示范2:

 三.malloc、new失败的区别

1.malloc失败

2.new失败

​编辑

四.内存泄漏

1.定义:

2.如何避免内存泄漏?


         前言:

         在学习C语言时,我们使用指针通过malloc/calloc/realloc函数去开辟堆区空间,这种方式称为动态开辟空间的方式

#include<stdlib.h>
//malloc——开辟一个只有四字节大小的整型空间,里面存放一个整型数据
	int* pa = (char*)malloc(sizeof(char) * 4);
	//开辟10个char型数据的字符型空间,且都初始化为'0'
	int* pc = (int*)calloc(sizeof(int) * 10,0);

        因为栈区空间很小(系统留给栈区的空间大小不多,大多都给了堆区),所以我们需要在堆区上去借用空间,既然是动态开辟,那么就需要手动去释放这块空间,否则会造成内存泄漏。而说到手动释放空间,则需要使用free函数,将这块空间的使用权限还给操作系统,于是这就不归我们管了,虽然我们可以强行访问这块空间,但属于非法行为!

    free(pa);
	free(pc);
	*pa = 10;	//非法访问,但不报错
    //方法:让指针置空,永远找不到那块空间即可
    pa=NULL;
    pc=NULL;

        而在C++中,诞生出了new和delete关键字,分别对应堆区空间的开辟和释放。

一..new关键字

      new使用格式1:动态开辟一个数据的堆区空间

类型*+变量名=new +类型;

例:

        代码解析:第一句是开辟一个4字节大小的空间,里面可以存放一个整型数据;第二句是开辟一个8字节大小的空间,里面可以存放一个浮点型数据。 

注:该种方式new出来的空间数据没有被初始化,所以获取的是随机值。

 类型*+变量名=new +类型(为空间数据赋值);

         在类型后一组括号内就可以为该空间数据赋值了。


格式2: 动态开辟多个数据的堆区空间——数组

 类型*+变量名=new +类型[填写开辟个数的数字];

        代码解析:动态开辟一块包含10个整型数据的堆空间;动态开辟一块包含15个浮点型数据的堆空间。这些开辟的数据仍都是随机值。

  类型*+变量名=new +类型[填写数字]+{为空间内的多个数据赋值};

        花括号可以为开辟的数组数据赋值。

总结:

        对于内置类型来说,malloc与new的动态开辟除了语法之外没有其他区别。

        2.堆区空间的开辟只能是动态进行的,没有静态开辟这一说


二.自定义类型的开辟

1.区别:

        对于自定义类型来说,new和delete关键字不仅会开辟空间,还会调用类型的构造函数和析构函数。

class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
}

    ~A()
{
	cout << "~A():" << this << endl;
}
private:
	int _a;
};


int main()
{
	cout << "malloc会调用构造与析构吗 ? " << endl;
	A* p1 = (A*)malloc(sizeof(A));
	cout << "new会调用构造与析构吗 ? " << endl;
	A* p2 = new A(1);
	free(p1);
	delete p2;
}
验证:

        经过验证发现:new和delete关键字会自动调用类的构造函数和析构函数。 

 2.匹配错误讲解:

        错误示范例1:

    int main(){
    A* p3 = new A[10];	
	delete p3;			
    return 0;
    }

        代码分析: 动态开辟了10个类型的空间,原本应该写delete[] p3;结果现在写成了delete p3;

运行后会出现错误,delete p3;只释放了一个空间。

        当执行new开辟时,编译器申请了10个空间,同时也会调用10个构造函数,delete p3后,因为析构函数是自己写的,编译器接收到指令只调用一次析构,而有10个构造就得调用10次析构,所以编译器不知道该怎么办了,会报错!造成内存泄漏

        若是不自己写析构函数,系统会自动生成默认的析构函数,编译器会智能判断且调用析构函数,它会将剩余开辟的空间也释放掉,结果也就不会报错了。 

delete[ ] 的执行原理:

        编译器会在释放的空间前再开辟几个字节空间,用来统计后面的对象个数,这时,编译器会根据个数去释放空间。 而delete p3会让系统认为只开辟了一个对象空间,根本不需要那个个数空间。对于delete种若是缺少了[],会出现释放不彻底问题,编译器认为指针指向的是某个数组元素,只会释放第一个元素空间,加了[ ],就能转化为指向数组的指针。

错误示范2:

int main(){
	A* p4 = new A;	
	delete[] p4;			
	return 0;
}

        delete[ ]会在p4开辟的位置前多开辟几个字节去存放开辟的类型数据个数,而p4只开辟了一个类型数据空间,根本就不需要那几个空间,所以会报错!delete关键字只释放了p4的空间,而对于前面多开辟的几个字节没有被释放,造成内存泄漏,报错!


三.malloc、new失败的区别

1.malloc失败

int main() {
	int* ptr = (int*)malloc(sizeof(int) * 1024*1024*1024*1024);
	if (ptr == nullptr) {
		perror("malloc fail");
		exit(0);
	}
	else {
		cout << ptr << endl;
	}
	return 0;
}

        malloc失败后会返回空指针! 

2.new失败

int main(){
    int* p = new int[100000*1024*5]{0};
	if (p) {
		cout<<p << endl;
	}
	else {
		cout << "申请失败" << endl;
	}
	return 0;
}

        new失败不会有返回值,会抛出异常! 


         

四.内存泄漏

1.定义:

       内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内 存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对 该段内存的控制,因而造成了内存的浪费。 内存泄漏的危害长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现 内存泄漏会导致响应越来越慢,最终卡死。

2.如何避免内存泄漏?

1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。

2. 采用RAII思想或者智能指针来管理资源。

3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。

4. 出问题了使用内存泄漏工具检测。例如:

 五:malloc/free与new/delete的整体区别:

共同点是:     都是从堆上申请空间,并且需要用户手动释放。

不同的点是1. malloc和free是函数,new和delete是操作符。

                       2. malloc申请的空间不能在定义时直接初始化,new可以。

                       3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可, 如果是多个对象,[ ]中指定对象个数即可。

                       4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型。

                       5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需 要捕获异常。

                       6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new 在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成 空间中资源的清理。

最后做一道练习题:

        使用 char* p = new char[100]申请一段内存,然后使用语句:delete p; 将该指针释放,执行代码时会发生什么?

        A.会有内存泄露

        B.不会有内存泄露,但不建议用

        C.编译就会报错,必须使用delete[ ]p

        D.编译没问题,运行会直接崩溃

 

        答案为B因为题中给出的指针p的类型为char*,它是内置类型,内置类型的指向堆区空间的指针释放——delete p 等价于free(p),所以即使指针p指向的堆区空间开辟的空间个数有若干也无所谓,使用delete p;语句也是正常的;

        但若是自定义类型,例如:A* p=new A[100];  使用delete p; 语句就会发生内存泄漏,导致运行报错,系统崩溃!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++基础——new和delete动态开辟 的相关文章

随机推荐

  • vue猜数字游戏

    div p msg p div
  • node.js JSON对象和string的相互转化

    JSON stringify obj 将JSON转为字符串 var json aa sdddssd bb 892394829342394792399 23894723984729374932874 cc 11111111111111 gt
  • html5期末知识点归纳总结,web期末考试知识点

    题型及知识点 一 知识点 上课内容全覆盖 除补充的html5和css3的内容 常用的html标记及属性 弄清楚哪些是块元素 哪些是行内元素 特殊字符标记 p40 Css属性 i 字体 font size font family font w
  • 蓝桥杯JAVA B组 2020(1)第五题 排序

    一 题目描述 小蓝最近学习了一些排序算法 其中冒泡排序让他印象深刻 在冒泡排序中 每次只能交换相邻的两个元素 小蓝发现 如果对一个字符串中的字符排序 只允许交换相邻的两个字符 则在所有可能的排序方案中 冒泡排序的总交换次数是最少的 例如 对
  • SQLCipher核心思想

    加密原理 page data iv hmac iv是一段随机数 可以保证每一页的iv值都不一样 和page data一起作用 用于生成hmac值 sizeof page data p
  • kubeasz 二进制安装k8s高可用集群

    一 kubeasz介绍 项目致力于提供快速部署高可用k8s集群的工具 同时也努力成为k8s实践 使用的参考书 基于二进制方式部署和利用ansible playbook实现自动化 既提供一键安装脚本 也可以根据安装指南分步执行安装各个组件 二
  • Maven2部署构件到Nexus时出现的Failed to transfer file错误

    原文出处 http www javatang com archives 2010 01 23 4518375 html 作者 Jet Mah from Java堂 声明 可以非商业性任意转载 转载时请务必以超链接形式标明文章原始出处 作者信
  • jni基础

    JNI相关 静态注册 java代码需要和C 代码相符通讯就需要通过JNI来进行注册 java public native String stringFromJNI 代表该函数的实现在so so Java com first firstndk
  • 内存管理方案

    内存管理方案 Memory Management System Author Owen 目 录 内存管理方案 1 目 录 1 1 概述 2 2 理论依据 2 2 1 不对内存进行管理 2 2 2 对内存进行内部管理 2 3 实现方案 2 3
  • RMI-Remote Method Invocation远程方法调用的使用

    手动启动服务方式 参考自 https www cnblogs com fanghao p 8918953 html 接口 import java rmi Remote import java rmi RemoteException publ
  • selenium二次封装

    使用到相关技术 selenium testng log4j maven selenium主要生成driver 获取页面元素等 testng主要Run程序 生成测试报告 管理用例执行顺序 断言等 log4j主要用来打印log日志 maven主
  • RuntimeError: CUDA unknown error - this may be due to an incorrectly set up environment

    解决方案 输入指令sudo shutdown r now即可重新启动驱动 如果还是无法解决则需要重新安装驱动
  • 【系统】C/C++内存管理之内存分配

    文章目录 0 内存分配方式 从静态存储区域分配 在栈上创建 在堆上分配 程序内存空间 1 C语言内存分配方式 静态与动态内存分配区别 1 1 静态分配方式 1 2 动态分配方式 1 malloc函数 2 calloc 函数 3 reallo
  • java.sql.SQLException: Incorrect string value: '\xF0\x9F\x92\x94' for column 'content' at row 1

    使用Mysql服务器的utf8字符编码 在存入移动端emoji表情时会报异常 Caused by java sql SQLException Incorrect string value xF0 x9F x98 x84 for column
  • 深度学习:实现mnist手写数字识别

    本文为 365天深度学习训练营 中的学习记录博客 原作者 K同学啊 接辅导 项目定制 我的环境 1 语言环境 Python 3 7 2 编译器 Pycharm 3 深度学习环境 TensorFlow2 5 一 前期工作 1 设置GPU 若使
  • Ubuntu20.04开机后,弹出检测到系统程序出现问题解决方法

    打开配置文件 sudo gedit etc default apport 然后 将其中的enable选项改成0 即可解决此问题
  • C++ *,&

    文章目录 语法 1 取地址运算符 2 间接寻址运算符 示例 1 可以累计使用间接寻址运算符来取消引用指向指针的指针 2 静态成员的地址 3 引用类型的地址 4 函数地址作为参数 4 示例 references 语法 1 取地址运算符 又称
  • c#量化交易_我用1天时间搭建自主量化交易(程序化交易)平台

    VirtualApi目前支持上海期货交易所的CTP回测 http www virtualapi cn 实盘期货 支持CTP http www kaihucn cn Simnow 上期CTP接口官方网站和模拟账户注册 http www sim
  • 【KITTI】KITTI数据集简介(一) — 激光雷达数据

    本文为博主原创文章 未经博主允许不得转载 本文为专栏 python三维点云从基础到深度学习 系列文章 地址为 https blog csdn net suiyingy article details 124017716 KITTI数据集的详
  • C++基础——new和delete动态开辟

    目录 前言 一 new关键字 格式2 动态开辟多个数据的堆区空间 数组 总结 二 自定义类型的开辟 1 区别 2 匹配错误讲解 错误示范例1 delete 的执行原理 错误示范2 三 malloc new失败的区别 1 malloc失败 2