malloc和new的区别
(1)malloc按字节开辟内存的,new开辟内存时需要指定类型,所以malloc开辟内存返回的都是void*,new直接返回类型指针
(2)malloc只负责开辟空间,new不仅有malloc的功能,还可以进行数据的初始化
(3)malloc开辟内存失败返回nullptr指针;new抛出bad_alloc类型的异常(所以不能用nullptr判断开辟开辟空间是否失败)
free和delete的区别
(1)delete:先调用析构函数,再释放内存,free只释放内存
注意:普通的数据类型new/malloc delete/free没区别,因为没有析构函数,比如 int* p;char * ch;
new和delete底层
(1)底层其实就是再调用重载运算符函数
new -> operator new
(重载运算符)的调用
delete->operator delete
(重载运算符)的调用
我们自己实现一下底层:
#include<iostream>
using namespace std;
//先调用operator new开辟内存空间,再调用对象的构造函数(初始化)
void* operator new(size_t size)
{
void* p = malloc(size);
if (p == nullptr)
throw bad_alloc();
return p;
}
//delete p; 先调用p指向对象的析构函数,再调用operator delete释放内存空间
void operator delete(void* ptr)
{
free(ptr);
}
int main()
{
try
{
int* p = new int;
delete p;
}
catch (const bad_alloc& err)
{
cerr << err.what() << endl;
}
return 0;
}
总结:
单个对象new:先调用operator new开辟内存空间,再调用对象的构造函数(初始化)
单个对象delete: 先调用p指向对象的析构函数,再调用operator delete释放内存空间
new[]和delete[]底层
#include<iostream>
using namespace std;
void* operator new[](size_t size)
{
void* p = malloc(size);
if (p == nullptr)
throw bad_alloc();
return p;
}
void operator delete[](void* ptr)
{
free(ptr);
}
int main()
{
try
{
int* q = new int[10];
delete[] q;
}
catch (const bad_alloc& err)
{
cerr << err.what() << endl;
}
return 0;
}
面试题
1. 如何检测内存泄漏
接管内存管理,重写operator new
和 operator delete
,使用映射表记录下申请了哪些内存释放了哪些内存,就可以很直观的看到哪些内存是没有被释放的
2.new/delete[]
和 new[]/delete
可以混用吗?C++为什么区分单个元素和数组的内存分配释放呢
(1)对于普通的编译器内置类型,没有构造和析构可言,只涉及内存开辟释放,内存开辟释放底层就是malloc和free,所以可以混用
(2)对于自定义类型,有析构函数,为了正确调用析构函数,再开辟数组的时候会多开辟4字节,记录对象个数,不能混用
下面进行详细解释假设有一个类:
class Test
{
public:
Test() { cout << "Test()" << endl; };
~Test() { cout << "~Test()" << endl; };
private:
int ma;
};
int main()
{
Test* p = new Test[5];
delete []p;
return 0;
}
在我们执行new Test[5]
操作的时候,实际上内存不止开辟 5*sizeof(Test)
字节的空间,而是会多开辟四字节的空间记录我们申请的对象个数,方便知道析构次数。
如图:
执行new Test[5]
会开辟5*sizeof(Test)+4
字节的内存空间,红色区域用来储存对象个数;new Test[5]
返回p地址(对象的起始地址)
void operator delete[](void* ptr)
传递p-4
地址,从p-4
内存地址开始释放(包含存储对象个数区域要一起释放)
这样即知道了需要调用几次析构函数,也可以给内存释放彻底
测试代码:
#include<iostream>
using namespace std;
void* operator new[](size_t size)
{
cout << "开辟空间大小:" << size << endl;
void* p = malloc(size);
if (p == nullptr)
throw bad_alloc();
cout << "开辟内存起始地址:" << p << endl;
return p;
}
void operator delete[](void* ptr)
{
cout << "释放起始地址:" << ptr << endl;
free(ptr);
}
class Test
{
public:
Test() { cout << "Test()" << endl; };
~Test() { cout << "~Test()" << endl; };
private:
int ma;//占四个字节
};
int main()
{
Test* p = new Test[5];
cout << "用户拿到的地址:" << p << endl;
delete[] p;
return 0;
}
下面这样是有问题的