new和delete的理解

malloc、free和new、delete的区别

cpp 复制代码
//malloc和free,称作c的库函数
//new和delete,称作运算符
/**********************************************/
//malloc按照字节开辟内存,返回值是void *,需要做类型强转
//仅开辟内存,不做初始化;malloc开辟内存失败,是通过返回值和nullptr比较
//new 是指定类型开辟内存,返回指定类型的类型指针;new不仅可以做内存开辟,还可以做内存初始化操作;new开辟内存失败是通过抛出bad_alloc的异常来判断的

int *p=(int *)malloc(sizeof(int));
if (p==nullptr)
{
	return -1;
}
*p=20;//需要单独进行初始化
free(p);

//等效的new和delete操作为
try
{
int *p1=new int (20);
}
catch(const bad_alloc &e)
{

}
delete p1;


//开辟数组类型的操作
int *q=(int *)malloc(sizeof(int)*20);
if (q==nullptr)
{
	return -1;
}
free(q);

int *q1=new int[20];//仅负责开辟内存
int *q1=new int[20]();//这种会初始化开辟的内存为零,不能传入其他值
delete []q1;

new的种类

cpp 复制代码
int *p1=new int(20);

int *p2=new (nothrow) int;//不抛出异常

const int *p3=new const int(40); //堆上开辟常量

//定位new
int data=0;
int *p4=new (&data) int(50);//指定在data的地址,划分出内存,并可以赋值
cout<<"data:"<<data<<endl;

new/delete 与 new\[\]/delete\[\] 不可混用的深层原因

首先重载new和delete 以及new \[\]和delete \[\]运算符

cpp 复制代码
//这里是全局运算符重载
//先开辟内存空间,再调用对象构造函数
void* operator new(size_t size)
{
	void* p = malloc(size);
	if (p == nullptr)
	{
		throw bad_alloc();
	}
	cout << "operator new addr:" << p << endl;
	return p;
}

//先析构对象,再清除内存空间
void operator delete(void* ptr)
{
	free(ptr);
	cout << "operator delete addr:" << ptr << endl;
}

/*构造数组类型*/
void* operator new[](size_t size)
{
	void* p = malloc(size);
	if (p == nullptr)
	{
		throw bad_alloc();
	}
	cout << "operator new[] addr:" << p << endl;
	return p;
}

void operator delete[](void* ptr)
{
	cout << "operator delete addr:" << ptr << endl;
	free(ptr);
}

情况1

随后自定义一个Test类,用以分析new和delete。

cpp 复制代码
//自定义的Test类,
class Test
{
public:
	Test()  { cout << "Test" << endl; }
	~Test() {  cout << "~Test" << endl; }
private :
	int x;
};
int main()
{	
	Test* p = new Test[5];
	cout << "Test类的大小:" << sizeof(Test) << endl;
	cout << "首个对象的地址:" << p << endl;
	delete[]p;
}

根据实验结果,可以发现开辟的内存其实地址和返回的首元素对象地址相差8个字节,这是因为当使用new\[\]的时候,编译器在开辟内存空间的时候会专门开辟8个字节(4或者8字节,根据编译器不同)用以存放数组的个数。所以这里开辟的内存空间应该是8+5*4=28个字节。

而如果在delete的时候,选择delete p,编译器认为这只是一个简单的对象,只是把数组中首元素进行了删除,而不是从p-8的位置开始释放,从而造成了内存泄露。

情况2

cpp 复制代码
int main()
{	
	Test* p = new Test();
	delete []p;
}

创建new的时候是一个普通变量,但是在delete删除的时候告诉编译器这是一个数组。

那么编译器就会从p-8(非法内存)的位置读取虚构的数组长度值,并从p-8的位置开始释放内存。但是p-8的位置不是当前p所指向的合法空间,这是越界访问,从而出错。

小结

对于普通的编译器内置类型

new 和delete\[\],new\[\] 和delete是可以混用的。
new \[\]和delete混用

new 和delete\[\]混用

**但是强烈不建议混用**!!!

对于自定义的类类型,存在析构函数,为了调用正确的析构函数,在开辟对象数组的时候,会多开辟4或8个字节,以记录对象的个数。

相关推荐
zh路西法9 分钟前
【玩转VLA具身智能机械臂】(四):点云分割与 OBB 夹取——从 PCA 失效到主动扫描
c++·机械臂·pca·vla·planner·obb·moveit2
地平线开发者19 分钟前
模型部署|征程6 量化路线怎么选:PTQ 还是 QAT?
神经网络·算法·机器学习
不会就选b29 分钟前
算法日常・每日刷题--<贪心>12
算法
爱和冰阔落31 分钟前
【Linux】条件变量为什么必须配合互斥锁:从 pthread_cond_wait 到阻塞队列
linux·运维·c++·缓存·中间件·安卓
初願致夕霞38 分钟前
Linux 进程间通信(IPC)机制详解:管道、FIFO 与 System V 共享内存
linux·服务器·c++
Tairitsu_H1 小时前
[C++] C++11 Lambda与包装器深度解析
开发语言·c++·c++11·lambda·包装器
傲世仙尊1 小时前
从磁盘硬件到Ext文件系统-Linux磁盘级文件系统学习笔记
linux·运维·服务器·开发语言·c++
小羊没烦恼!1 小时前
Memory 记忆设计讨论:为什么 Agent Memory 不能只靠向量数据库?
java·开发语言·windows·算法·c#
FlightYe1 小时前
音视频修炼之编码器(一):AVC、HEVC编码器内部
android·linux·c++·音视频
羑悻的小杀马特2 小时前
数据织网者:Jsoncpp库深度解析——从C++原生JSON处理到工程级数据交互实战全攻略
c++·json·交互·jsoncpp·原生库