📋 个人简介
在C语言时,我们学习了一些动态开辟内存的方式比如malloc, calloc, realloc等等,但是这些函数不太能满足C++的需求。比如一个自定义类型,如果用C语言的函数去开辟一个空间,创建完成后我们还需要手动去对块空间去进行初始化,这样就不太方便,于是C++创造出了new 和 delete。
内存分布
在学习如何去开辟一片空间之前,我们先来复习一下C语言中是如何划分内存空间的,在C语言中内存大致可以分为六个区域,自上而下分别为:内核空间,栈区,内存映射区,堆区,数据段,代码段
光说不练假把式,我们来看一个题目,来检验一下大家对于这块的知识还记得多少。
上代码。
int c = 1;
void Test() {
static int a = 1;
int b = 1;
int num[10] = { 1,2,3,4 };
char char1[] = "abcd";
char* pchar2 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}
1.选择题
选项: A.栈 B.堆 C.数据段 D.代码段
a在哪里?____________ b在哪里?____________
c在哪里?____________ num1在哪里?____________
char1在哪里?____________ *char1在哪里?____________
pchar2在哪里?____________ *pchar2在哪里?____________
ptr1在哪里?____________ *ptr1在哪里?____________
2.填空题
sizeof(num) = ____________
sizeof(char1) = ____________ strlen(char1) = ____________
sizeof(pchar2) = ____________ strlen(pchar2) = ____________
sizeof(ptr1) = ____________
请大家仔细思考后,再看下方的答案。
1.选择题
选项: A.栈 B.堆 C.数据段 D.代码段
a在哪里? C b在哪里? A
c在哪里? C num1在哪里? A
char1在哪里?A *char1在哪里?A
pchar2在哪里?A *pchar2在哪里?C
ptr1在哪里?A *ptr1在哪里?B
2.填空题
sizeof(num) = 40
sizeof(char1) = 5 strlen(char1) = 4
sizeof(pchar2) = 4/8 strlen(pchar2) = 4
sizeof(ptr1) = 4/8
你答对了多少呢?
C++内存管理方式
new和delete是C++中新引入的两个关键字,一个是用来开辟内存的,一个是用来释放内存的。
内置类型
int main() {
//开辟一个int的空间
int* p1 = new int;
//开辟一个int的空间,并将值初始化为10
int* p2 = new int(10);
//开辟一个3个int的空间
int* p3 = new int[3];
delete p1;
delete p2;
delete[] p3;
return 0;
}
new和delete的两种使用方式:
对于申请和释放单个空间,使用new和delete。
书写方式:(类型*)变量名 = new 类型 (初始化的值),delete 变量名。
对于申请和释放多个空间,使用new[]和delete[]。
书写方式:(类型*)变量名 = new 类型 [个数], delete[]变量名。
多个空间无法使用()号在new后指定初始化的值,只可采取:
int *p = new int[10]();
将这一片空间初始化为0。
自定义类型
对于自定义类型,new和delete在使用上大体相似,只是开辟空间时回去调用自定义类型的构造函数,而销毁空间时会去调用自定义类型的析构函数。
new和delete
operator new 和 operator delete
在C++中,new和delete是操作符,而operator new和operator delete是两个全局函数,new操作符是通过调用operator new来开辟空间的,delete操作符是通过调用operator delete来销毁空间的。
vs2013下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
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
抛异常是C++中一种处理程序问题的方式,我们以后会讲。大家可以看到源码中开辟内存是使用C语言中的malloc来开辟的空间,这就是为什么内置类型使用new和C语言中使用malloc开辟空间没什么区别的原因。
首先我们先来看看C语言中的free是什么:
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
我们可以看到我们在C语言中使用的free,其实是一个宏,在程序编译完成后会被替换成_free_dbg函数,而此时我们再看看operator delete的源码
vs2013中 operator delete源码:
我们可以看代码中标黄的部分,我们就会惊奇的发现,其实C++中的opeator delete 也用的是C语言中的free函数。
new 和 delete实现原理
了解了operator new 和 operator delete 我们再来回到最开始的地方,聊聊new 和 delete 具体是怎么实现的。
- new的原理
- 使用operator new 函数申请空间。
- 在申请上的空间上调用对应类型的构造函数。
- delete的原理
- 在开辟的空间上调用对象的析构函数,完成资源清理的工作。
- 调用 operator delete 释放片空间
- new[] 的实现原理
- 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请。
- 在申请空间上使用N次构造函数。
- delet[] 的实现原理
- 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理。
- 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间。
定位表达式new
定位new表达式是在已分配的原始空间中使用构造函数初始化一个对象。
使用格式:new(指针)类型
定位表达式new就像盖房子,土地已经给你准备好了,但是土地如何使用还没定,而定位表达式new就是告诉你土地如何使用的。
class Date
{
public:
Date(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << " " << _month << " " << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main() {
//此时p并不是一个完整的对象,他的值为随机值
Date* p = (Date*)malloc(sizeof(Date));
//调用Date的构造函数进行初始化。
new(p) Date(1000, 1, 1);
return 0;
}
C和C++动态内存开辟区别
- malloc和free是函数,new和delete是操作符。
- malloc申请的空间不会初始化,new可以初始化。
- malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可。
- malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型。
- malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常。
- 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。
结语
欢迎各位参考与指导!!!❤️