C++的 new 和 delete

news2024/9/22 15:27:16

文章目录

  • 一、new 和 delete 的使用
  • 二、operator new 和 operator delete 函数
  • 三、new 和 delete 的实现原理
  • 四、申请空间和释放空间应配套使用
  • 五、定位 new 表达式
  • 六、malloc/free 和 new/delete 的区别

C语言的动态内存管理函数(malloc、calloc、realloc、free) 虽然可以继续在 C++ 使用,但是对于自定义类型成员而言,这些函数不会自动调用构造函数和析构函数,于是 C++ 增加了 new 和 delete 关键字

一、new 和 delete 的使用

new 和 delete 用于在堆上申请或释放一个元素的空间,new[] 和 delete[] 用于在堆上申请或释放一块连续的空间,对于自定义类型空间的开辟,new 和 delete 还会调用构造函数和析构函数

#include <iostream>

using namespace std;

class Demo
{
public:
	Demo(int a1 = 10, int a2 = 20)
		: _a1(a1)
		, _a2(a2)
	{
		cout << "Demo()" << endl;
	}

	void print()
	{
		cout << _a1 << " " << _a2 << endl;
	}

	~Demo()
	{
		cout << "~Demo()" << endl;
	}

private:
	int _a1;
	int _a2;
};

void printIntArr(int* arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

void printDemoArr(Demo* arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		arr[i].print();
	}
	cout << endl;
}

int main()
{
	//用 new 申请一个内置类型变量的空间
	int* pint1 = new int;
	cout << *pint1 << endl; //输出 -842150451

	//使用括号中的值初始化变量
	int* pint2 = new int(5);
	cout << *pint2 << endl;	//输出 5

	//用 delete 释放一个变量的空间
	delete pint1;
	delete pint2;

	//用 new 申请一个自定义类型对象的空间,申请后会自动调用构造函数
	Demo* pd1 = new Demo;	//输出 Demo()
	pd1->print();	//输出 10 20

	//自定义类型会根据括号中的参数调用对应的构造函数
	Demo* pd2 = new Demo(5, 5);	//输出 Demo()
	pd2->print();	//输出 5 5

	//用 delete 释放一个变量的空间,释放前会自动调用析构函数
	delete pd1;	//输出 ~Demo()
	delete pd2;	//输出 ~Demo()


	//对内置类型用 new[] 开辟一块连续的空间
	int* pint3 = new int[5];	//[]中表示开辟整形的个数
	printIntArr(pint3, 5);	//输出 -842150451 -842150451 -842150451 -842150451 -842150451

	//用花括号中的值初始化开辟的连续空间,未给值的为 0
	int* pint4 = new int[5]{ 1, 2, 3, 4 };	
	printIntArr(pint4, 5);	//输出 1 2 3 4 0

	//对内置类型用 delete[] 释放一块连续的空间
	delete[] pint3;
	delete[] pint4;
	
	//对自定义类型用 new[] 开辟一块连续的空间
	//申请后会对空间自动调用构造函数 5 次
	Demo* pd3 = new Demo[5];	//输出 5 行 Demo()
	printDemoArr(pd3, 5);	//输出 5 行 10 20

	//用花括号中的值初始化开辟的连续空间
	//花括号中如果用小括号会被认为是逗号表达式,会去调用单参的构造函数
	//调用多参构造函数应在花括号中使用花括号,未给的值根据构造函数决定
	Demo* pd4 = new Demo[5]{ (1, 2), {5}, {5, 10}};	//输出 5 行 Demo()
	printDemoArr(pd4, 5);	//输出 第一行 2 20,第二行 5 10 第三行 5 10,两行 10 20

	//对自定义类型用 delete[] 释放一块连续的空间
	//释放之前会对空间自动调用析构函数 5 次
	delete[] pd3;	//输出 5 行 ~Demo
	delete[] pd4;	//输出 5 行 ~Demo

	return 0;
}

二、operator new 和 operator delete 函数

operator new 和 operator delete 是系统提供的全局函数,不是 new 和 delete 的运算符重载函数

  • operator new 底层是通过 malloc 函数来申请空间,当空间申请成功时直接返回,失败时抛出异常(不会返回 nullptr),operator new 函数可以像 malloc 一样使用,只是失败时的处理不同

  • operator delete 和 free 底层都是是通过 _free_dbg 函数释放空间,只不过 operator delete 会对释放前后进行一些检查

#include <iostream>

using namespace std;

int main()
{
	//operator new 和 malloc 使用方法一样
	//operator new 申请空间失败时抛异常
	int* pi = (int*)operator new(sizeof(int) * 4);
	
	//operator delete 和 free 使用方法一样,都会调用 _free_dbg
	//operator delete 在释放空间时会做一些检查
	operator delete(pi);

	return 0;
}

在这里插入图片描述

operator new[] 和 operator delete[] 也是系统提供的全局函数,内部是通过调用 operator new 和 operator delete 函数
在这里插入图片描述

三、new 和 delete 的实现原理

如果是内置类型,new 和 delete 调用 operator new 和 operator delete,new[] 和 delete[] 调用 operator new[] 和 operator delete[]

如果是自定义类型:

#include <iostream>

using namespace std;

class Demo
{
public:
	Demo(int a1 = 10, int a2 = 20);

	~Demo();

private:
	int _a1;
	int _a2;
};

Demo::Demo(int a1, int a2)
	: _a1(a1)
	, _a2(a2)
{
	cout << "Demo()" << endl;
}

Demo::~Demo()
{
	cout << "~Demo()" << endl;
}

int main()
{
	Demo* pd1 = new Demo(5, 5);
	delete pd1;

	Demo* pd2 = new Demo[2]{ {1, 2}, {2, 3} };
	delete[] pd2;

	return 0;
}
  • new:
    1. 调用 operator new 函数申请空间
    2. 在申请的空间上执行构造函数,完成对象的构造
  • delete:
    1. 在空间上执行析构函数,完成对象中资源的清理工作
    2. 调用operator delete函数释放对象的空间

在这里插入图片描述

  • new 类型[N]:
    1. 调用operator new[] 函数,实际上是在 operator new[] 中调用 operator new 函数完成 N 个对象空间的申请
    2. 在申请的空间上执行 N 次构造函数
  • delete[]:
    1. 在释放的对象空间上执行 N 次析构函数,完成 N 个对象中资源的清理
    2. 调用 operator delete[] 释放空间,实际上时在 operator delete[] 中调用 operator delete 来释放空间

在这里插入图片描述

四、申请空间和释放空间应配套使用

malloc/free、new/delete、new[]/delete[] 需要配套使用,否则总会有出问题的时候

下述代码不会报错,会产生内存泄漏

#include <iostream>

using namespace std;

class Stack
{
public:
	Stack(int capacity = 4)
		: _a(new int[capacity])
		, _top(0)
		, _capacity(capacity)
	{

	}

	~Stack()
	{
		if (_a)
		{
			delete[] _a;
			_top = _capacity = 0;
		}
	}

private:
	int* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack* ps = new Stack;
	
	//free(ps);	//内存泄漏

	//delete 释放内存之前会调用析构函数
	delete ps;	//正确写法

	return 0;
}

在这里插入图片描述

下述代码在 vs2022 下会崩溃

#include <iostream>

using namespace std;

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

	~A()
	{
		cout << "~A():" << endl;
	}

private:
	int _a;
};

int main()
{
	A* p1 = new A[10];
	//free(p1); 	//崩溃

	delete[] p1;	//正确写法

	A* p2 = new A[10];
	//delete p2;	 //崩溃

	delete[] p2;	//正确写法

	return 0;
}

在这里插入图片描述

注意:不同的编译器处理可能不同,这里只代表在 vs2022 编译器中

五、定位 new 表达式

定位 new 表达式是在已开辟好的原始内存空间上调用构造函数初始化一个对象,使用格式:

new(place_address)type 或者 new(place_address)type(initializer-list)
place_address 必须是一个指针,initializer-list 是类型的初始化列表

定位 new 表达式在实际中一般是配合 内存池 使用,因为内存池分配出的内存没有初始化,并且构造函数不可以显示调用,所以如果是自定义类型的对象,需要使用定位 new 以进行显示调用构造函数进行初始化

#include <iostream>

using namespace std;

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

private:
	int _a;
};

//定位 new 又叫 replacement new
int main()
{
	//p1 现在指向的只是与 A 对象相同大小的一段空间,并不是一个对象,因为没有调用构造函数
	A* p1 = (A*)malloc(sizeof(A));
	new(p1)A;	//调用无参的构造函数 输出 A()

	//可以手动调用析构函数,然后释放空间
	p1->~A();	//输出 ~A()
	free(p1);

	//p2 现在指向的只是与 A 对象相同大小的一段空间,并不是一个对象,因为没有调用构造函数
	A* p2 = (A*)operator new(sizeof(A));
	new(p2)A(10);	//10 是参数,可以根据参数调用对应的构造函数 输出 A()

	p2->~A();	//输出 ~A()
	operator delete(p2);
	
	return 0;
}

六、malloc/free 和 new/delete 的区别

malloc/free 和 new/delete 的共同点是:都是从堆上申请空间,并且需要用户手动释放

不同的地方是:

  • malloc 和 free 是函数,new 和 delete 是运算符
  • malloc 申请的空间不会初始化,new 可以初始化
  • malloc 申请空间时,需要手动计算空间大小并传递,new 只需在其后跟上空间的类型,如果是多个对象,[] 中指定对象个数即可
  • malloc 的返回值为 void*,接收时必须强制类型转换,new 不需要,因为 new 后跟的是空间的类型
  • malloc 申请空间失败时,返回的是NULL,因此使用时必须判空,new 不需要,但是 new 需要捕获异常
  • 申请自定义类型对象时,malloc/free 只会开辟空间,不会调用构造函数与析构函数,而 new 在申请空间后会调用构造函数完成对象的初始化,delete 在释放空间前会调用析构函数完成空间中资源的清理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/343083.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python for 循环语句

Python for 循环语句Python for循环可以遍历任何序列的项目&#xff0c;如一个列表或者一个字符串。语法&#xff1a;for循环的语法格式如下&#xff1a;for iterating_var in sequence:statements(s)流程图&#xff1a;实例&#xff1a;实例#!/usr/bin/python# -*- coding: UT…

Hudi-简介和编译安装

简介 简介 Apache Hudi&#xff08;Hadoop Upserts Delete and Incremental&#xff09;是下一代流数据湖平台。Apache Hudi将核心仓库和数据库功能直接引入数据湖。Hudi提供了表、事务、高效的upserts/delete、高级索引、流摄取服务、数据集群/压缩优化和并发&#xff0c;同…

【yolov5】首次尝试目标检测利用prompt(完整操作流程)

1、打开prompt 2、切换到pytorch所在环境 conda activate freezing我的环境名是freezing&#xff0c;这里根据自己环境名去激活切换 3、进入到yolov5项目所在路径 激活完环境后立即执行指令当然是无效的&#xff0c;首先要进入到你的项目目录 首先看一下自己的项目在那个位…

https协议

文章目录对称加密方案非对称加密方案对称加密方案非对称加密方案对称加密方案非对称加密方案数字证书因为HTTP是明文传输&#xff0c;所以会很有可能产生中间人攻击&#xff08;获取并篡改传输在客户端及服务端的信息并不被人发觉&#xff09;&#xff0c;HTTPS加密应运而生。 …

【Java|golang】1234. 替换子串得到平衡字符串---双指针

有一个只含有 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符&#xff0c;且长度为 n 的字符串。 假如在该字符串中&#xff0c;这四个字符都恰好出现 n/4 次&#xff0c;那么它就是一个「平衡字符串」。 给你一个这样的字符串 s&#xff0c;请通过「替换一个子串」的方式&#xff0c;…

SpringBoot2知识点记录

SpringBoot2知识点记录1.SpringBoot2基础入门1.1 环境要求1.1.1 maven设置1.2 第一个程序 HelloWorld1.2.1 创建maven工程1.2.2 引入依赖1.2.3 创建主程序1.2.4 编写业务1.2.5 测试1.2.6 简化配置1.2.7 简化部署1.3 自动装配1.3.1 SpringBoot特点1.3.1.1 依赖管理1.3.1.2 自动装…

上班在群里摸鱼,逮到一个字节8年测试开发,聊过之后羞愧难当...

老话说的好&#xff0c;这人呐&#xff0c;一旦在某个领域鲜有敌手了&#xff0c;就会闲得某疼。前几天我在上班摸鱼刷群的时候认识了一位字节测试开发大佬&#xff0c;在字节工作了8年&#xff0c;因为本人天赋比较高&#xff0c;平时工作也兢兢业业&#xff0c;现在企业内有一…

【论文阅读】基于意图的网络(Intent-Based Networking,IBN)研究综述

IBN研究综述一、IBN体系结构1.1 体系结构&#xff1a;1.2 闭环流程&#xff1a;1.3 IBN的自动化程度(逐步向前演进)&#xff1a;二、IBN 的实现方式2.1 意图获取&#xff1a;2.1.1 YANG、NEMO2.1.2 Frenetic、NetKAT、LAI2.2 意图转译&#xff1a;2.2.1 iNDIRA系统2.2.2 基于模…

GIT命令操作大全

文章目录一、前言二、工作模块2.1 Workspace&#xff1a;工作区2.2 Index / Stage&#xff1a;暂存区2.3 Repository&#xff1a;本地仓库2.4Remote&#xff1a;远程仓库三、GIT基本配置四、GIT项目代码管理4.1 初始化git仓库4.2 提交到暂存区(stage)4.3 将暂存区的文件恢复到工…

解决HC-05/HC06等蓝牙模块的调试问题

解决HC-05/HC06等蓝牙模块的调试问题问题&#xff1a;1.无法使用USB转串口工具设置HC-05等蓝牙模块&#xff0c;具体问题是&#xff1a;发送AT指令&#xff0c;无回复&#xff1b;2.电脑如何连接HC-05模块&#xff0c;与模块通信&#xff08;具体场景&#xff1a;HC-05模块的串…

python学习之pyecharts库的使用总结

pyecharts官方文档&#xff1a;https://pyecharts.org//#/zh-cn/ 【1】Timeline 其是一个时间轴组件&#xff0c;如下图红框所示&#xff0c;当点击红色箭头指向的“播放”按钮时&#xff0c;会呈现动画形式展示每一年的数据变化。 data格式为DataFrame&#xff0c;数据如下图…

数据结构——TreeMap、TreeSet与HashMap、HashSet

目录 一、Map 1、定义 2、常用方法 3、注意 二、TreeMap 三、HashMap 1、定义 2、冲突定义 3、冲突避免方法——哈希函数设计 &#xff08;1&#xff09;、直接定制法(常用) &#xff08;2&#xff09;、除留余数法(常用) &#xff08;3&#xff09;、平方取中法 &…

kubectl命令控制远程k8s集群(Windows系统、Ubuntu系统、Centos系统)

文章目录1. 本地是linux2. 本地是Windows1. 本地是linux 安装kubectl命令 法一&#xff1a;从master的/usr/bin目录下拷贝kubectl文件到本机/usr/bin目录下法二&#xff1a;GitHub下载kubectl文件 在家目录下创建.kube目录config文件 法一&#xff1a;将master上对应用户的~/.…

binlog、redolog、undolog

目录 一、binlong 1、binlog介绍 作用 特点 2、日志格式 3、写入流程 二、redo log 1、redo log作用 2、redo log如何写入 3、怎么提交数据进行刷盘 4、奔溃恢复流程 三、undolog 一、binlong 1、binlog介绍 作用 binlog是 mysql server 层的一种二进制日志&…

GA-PEG-GA,Glutaric Acid-PEG-Glutaric Acid,戊二酸-聚乙二醇-戊二酸供应

英文名称&#xff1a;Glutaric Acid-PEG-Glutaric Acid&#xff0c;GA-PEG-GA 中文名称&#xff1a;戊二酸-聚乙二醇-戊二酸 GA-PEG-GA是一种线性双功能PEG羧酸试剂。PEG和羧基COOH之间存在C4酯键。PEG羧酸可用于与氨基反应&#xff0c;与NHS和DCC、EDC等肽偶联试剂反应。 P…

CUDA原子操作|参加CUDA线上训练营

什么是原子操作 CUDA的原子操作可以理解为对一个Global memory或Shared memory中变 “读取-修改-写入” 这三个操作的一个最小单位的执行过程&#xff0c;在它执量进行行过程中&#xff0c;不允许其他并行线程对该变量进行读取和写入的操作。 基于这个机制&#xff0c;原子操…

实测2023款哪吒U-II,智驾功能对女司机很友好

最近&#xff0c;我们受邀试驾了2023款哪吒U-II。这是一款A级新能源SUV&#xff0c;是哪吒U的改款车型。哪吒U系列自2020年3月上市到2023年1月&#xff0c;累计销售数量达76688台&#xff0c;也因此被称为15万级智能天花板。2023款哪吒U-II的一大亮点是&#xff1a;针对以往哪吒…

react基础,操作属性样式事件。事件处理this指向问题,及案例

1.1 React 起源于 Facebook 于2013年5月开源. 是一个用于构建用户界面的 JavaScript 库, 与vue,angular并称前端三大框架&#xff0c;现在最新版本是18 特点&#xff1a;-数据驱动&#xff08;操作数据&#xff09; - 组件化 - 虚拟DOM 开发环境&#xff1a; cdn引入js <…

Theano教程:Python的内存管理

在写大型程序时候的一大挑战是如何保证最少的内存使用率。但是在Python中的内存管理是比较简单的。Python显示分配内存&#xff0c;使用引用计数系统管理对象&#xff0c;当指向某一个对象的引用数变为 0 的时候&#xff0c;该对象所占的内存就会被释放。理论上听起来很不错&am…

Linux基础语法进阶版

Linux基础语法 查看文件内容指令 touch 主要是修改文件时间&#xff0c;多用创建文件 -a #只更改访问时间 -m #只更改修改时间 -c --no-create#不创建任何文件cat 展示小文件内容 -b #对于非空输出行编号 -n #对于所有行输出编号 -E #在每行结束处显示"$" -A #展示所…