目录
1.c/c++内存分布
2.new与delete/malloc与free
c++内存管理方式:
new/delete操作内置类型:
new/delete操作自定义类型
operator new与operator delete函数
new和delete的实现原理
内置类型
自定义类型
malloc/free和new/delete的区别
在学习c语言的时候我们就已经了解到不同的数据存储在不同的空间当中,对于内存,其被划分成不同的各个区域,分别用来管理不同的数据,如下图:
其次, c++在继承c语言的基础上,对于堆区上的申请和释放做了优化,这将体现在c++类和自定义数据类型中。
首先我们知道如何利用c语言的方式向堆区上开辟空间,我们利用malloc,cealloc,realloc这三个函数可以实现向堆区上开辟一块空间,之后我们在进行初始化,在对空间的利用完之后,利用free函数释放掉对上的空间,实现动态内存管理。
int main()
{
char* p1 = (char*)malloc(sizeof(char)*10);
free(p1);
char* p2 = (char*)calloc(char ("hello world"), sizeof(char)*10);
char *p3 = (char*)realloc(p2, sizeof(char) * 10);
free(p3);
return 0;
}
对于c语言的动态内存管理:
1.首先大佬认为如此的设计使得动态内存管理过于冗余,对于空间的开辟较为麻烦。
2.我们需要强制类型转换并计算出所需该类型的大小。
我们能看到对于new的一些操作及运算符重载。
对于c++:我们可以利用new来进行动态内存的申请,利用delete进行内存的释放。
void Test()
{
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[3];
//动态申请5个int类型的空间并初始化
int *ptr7=new int[5]{1,2,3,4,5};
delete ptr4;
delete ptr5;
delete[] ptr6;
delete[] ptr7;
}
可以看到我们可以初始化空间,并且对于多个内存的开辟也更加方便,对于大小可以自己推导,如一个整型四个字节,一个字符型一个字节。
注意:这里在开辟多个或一个空间时,delete的写法也应与之对应,否则存在释放报错。
因为c++兼容c语言,所以除了书写简便,对于内置类型,功能是一样的。
如果仅仅是因为优化写法,那么在c++设计new和delete是纯属没有必要的,真正牛逼得地方是new与delete可以操作自定义类型!!!而这与c++11类和对象密切联系着。
利用malloc与free我们只能做到对空间的开辟与释放,但无法去初始化这个自定义类型的空间。
如自定义类型A:
class A
{
public:
A(int a = 0)
: a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
void init_a(int x)
{
_a = x;
}
private:
int a;
};
对于自定义类型下的malloc:
void test1()
{
A* p1 = (A*)malloc(sizeof(A));
//无法调用构造函数来初始化,因为当前无法显式调用构造函数
p1->_a = 5;//也无法对私有数据操作,只能通过定义接口函数来初始化
p1->init_a(5);//这也是对于简单的数据,若是设计的类中数据量偌大,则直接就g了
free(p1);
}
我们可以看到自定义类型的数据要实现初始化太困难且复杂,甚至没法用。
那么对于重新设计的new与delete:
int main()
{
// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间
//还会调用构造函数和析构函数
A* p1 = new A;
A* p2 = new A(5);
A* p3 = new A[2]{1,2};
delete p1;
delete p2;
delete[]p3;
return 0;
}
对于初始化new可以再申请空间后调用构造函数,对于释放delete会调用析构函数,之后在释放空间。
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
比特就业课
通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果
malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施
就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
5. new和delete的实现原理
5.1 内置类型
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
void operator delete(void *pUserData)
{
_CrtMemBlockHeader * pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg( pUserData, pHead->nBlockUse );
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
/*
对于这里的new内存开辟失败的时候,这里设计利用异常进行判断。
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void *pUserData)
{
_CrtMemBlockHeader * pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg( pUserData, pHead->nBlockUse );
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)