目录
承接CD30.【C++ Dev】类和对象(21) 内存管理(上)文章
1.new和delete的底层实现
测试代码分析
以下代码在VS2010上测试:
class Myclass
{
public:
Myclass()
{}
~Myclass()
{}
int _val;
};
int main()
{
Myclass* ptr= new Myclass;
delete ptr;
return 0;
}
观察现象
单步调试,进入new的细节部分
发现调用了operator new,在operator new里面调用了malloc函数
但能说明new等价为operator new吗?
可以写代码验证看看:
按operator new的函数定义来看,参数写定义类型的大小,返回类型为void*,可能需要强制类型转换
为Myclass添加一个成员变量,如下:
class Myclass
{
public:
Myclass()
:_val(1)
{}
~Myclass()
{}
int _val;
};
int main()
{
Myclass* ptr= (Myclass*)operator new (sizeof(Myclass));
delete ptr;
return 0;
}
下断点到delete ptr,监视窗口看看*ptr的_val,发现是随机值(未初始化),则得出结论:operator new没有调用构造函数
单步调试,进入delete的细节部分
发现调用了Myclass::`scalar deleting destructor'
进入Myclass::`scalar deleting destructor'后,发现调用了析构函数和operator delete
结论: new等价为operato new和构造函数(先申请空间再构造),operato new调用了malloc,delete等价为operator delete和析构函数(先析构清理资源再释放空间),operator delete调用了free(operato new和operator delete是系统提供的全局函数)
注:operator new[]实际多次调用operator new和构造函数,operator delete[]实际多次调用析构函数和operator delete
可由此推出:内置类型没有构造函数,因此new和operator new的作用是一样的,例如以下代码:
int* ptr1 = (int*)operator new(sizeof(int));
operator delete (ptr1);
int* ptr2 = new int;
delete ptr2;
(ptr1和ptr2在内存申请和释放上完全等价)
2.new申请空间失败的处理方法
malloc申请空间如果失败,会返回空指针,但是new和malloc不同,new申请空间失败会抛异常
(如果malloc申请空间,成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常)
面向对象语言处理失败,不太会使用返回值,而是会抛异常!
VS2010的operator new的代码:
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{ // try to allocate size bytes
void* p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{ // report no memory
static const std::bad_alloc nomem;//抛异常
_RAISE(nomem);
}
return (p);
}
异常的捕获方法try和catch
当前先简单了解用法即可
例如有以下内存泄漏代码:
#include <iostream>
using namespace std;
int main()
{
int* ptr;
while (1)
{
ptr = new int[1024 * 1024 * 1024];
}
return 0;
}
程序运行一会就强行退出了:
使用try和catch捕获异常:
#include <iostream>
using namespace std;
int main()
{
int* ptr;
try
{
while (1)
{
ptr = new int[1024 * 1024 * 1024];
}
}
catch (const exception& e)
{
cout << e.what() << endl;
}
return 0;
}
运行结果:控制台窗口打印异常信息
3.一个例子理解new和delete的操作细节
提问:
1.Mystack* ptr = new Mystack;的new做了什么?
2.delete ptr;的delete做了什么?
#include <iostream>
using namespace std;
typedef int DataType;
class Mystack
{
public:
Mystack(size_t capacity = 5)
{
cout << "构造函数: Mystack(size_t capacity = 5)" << endl;
_arr = (DataType*)malloc(sizeof(DataType) * capacity);
_capacity = capacity;
_size = 0;
}
~Mystack()
{
cout << "析构函数: ~Mystack()" << endl;
if (_arr)
{
free(_arr);
_arr = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _arr;
int _capacity;
int _size;
};
int main()
{
try
{
Mystack* ptr = new Mystack;
delete ptr;
ptr = nullptr;
}
catch (const exception& e)
{
cout << e.what() << endl;
}
return 0;
}
(注: delete ptr只是释放资源,不会自动将ptr置空,为了避免野指针的问题,应该手动将ptr置为空)
分析
Mystack* ptr = new Mystack:
由之前的结论可知:new先申请空间再构造
先在堆区上为栈对象Mystack开辟空间,ptr在栈区上,且ptr指向Mystack对象,如下图:
(注:此时_arr仍然是野指针,暂时没有调用Mystack的构造函数)
调用构造函数后,如下图:
完整的图为:
delete ptr + ptr=nullptr
delete ptr
先调用析构函数~Mystack():释放_arr指向的空间,并为_arr置空,将_capacity和_size清零
再调用free()释放对象的空间:
(此时ptr为野指针)
ptr=nullptr
delete ptr只是释放资源,不会自动将ptr置空,为了避免野指针的问题,应该手动将ptr置为空
其他提醒
构造函数Mystack()中不用判断_arr是否为nullptr,因为new申请失败会抛异常,只需要使用try和catch捕获即可
int main()
{
try
{
Mystack* ptr = new Mystack;
delete ptr;
ptr = nullptr;
}
catch (const exception& e)
{
cout << e.what() << endl;
}
return 0;
}
程序运行结果
下篇将介绍定位new表达式以及new和malloc,dele和free的区别总结