主要内容:
- C/C++内存布局
- new和malloc的区别
- delete和free的区别
C/C++内存布局 C/C++内存分为如下的几个部分:
1.栈区 存放临时的局部变量 2.堆区 malloc,realloc,calloc申请的动态内存 3.静态区 存储全局变量和局部静态变量 4.常量区(代码段) : 存储常量的区域
首先我们要知道为什么要对内存区域进行划分。最重要的一个原因就是为了方便管理数据,因为计算机世界最重要的资源就是数据。对于数据更好 的管理就显得十分有必要!而划分内存区域就是这样的一种手段。 来看一看下面这些变量分别存储在什么地方:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
static int g_val = 10;
void exam()
{
static int s_val = 20;
int val = 5;
char arr[] = "hello world";
const char* pa = "hello world";
int* a = (int*)malloc(sizeof(int) * 5);
free(a);
}
int main()
{
exam();
return 0;
}
g_val ,s_val ,val在什么区域? arr在哪个区域,*arr在什么区域? p在哪个区域,*p在哪个区域? a在哪一个区域,*a在哪个区域?
答案是:g_val ,s_val在静态区,arr,*arr在栈区 p在栈区,*p在常量区 , a在栈区,a在堆区 可能比较容易出错的就是arr和arr这两个地方
char arr[]="abcdef";
了解了这个C/C++的内存布局,接下来我们来看new和malloc的区别。
new和malloc的区别 首先,我们知道C++兼容C语言,也就是C语言那套内存管理机制我们在C++里面仍然还可以继续使用。但是C++又引入了自己的新机制—>new关键字来替代malloc,那么new和malloc究竟有什么区别。new又应该怎么使用呢?
void test()
{
int* p = new int;
int* pa = new int[10];
int* ppa = new int(10);
}
那么这时候不难可以看出。这里的new和malloc并没有太大的区别,确实是这样!对于内置类型来说,使用new和使用malloc几乎没有区别。真正不同的地方是在于对于自定义类型的处理!
class A
{
public:
A(int a=0)
:_a(a)
{}
private:
int _a;
};
void show()
{
A* pa = (A*)malloc(sizeof(A));
A* ppa = new A;
}
从调试窗口不难可以看出,malloc只是申请了一块空间,对于空间里面的内容不做任何处理!而new在申请空间的同时,还还调用了构造函数!所以对于自定义类型来说,new会在申请空间的同时调用构造函数初始化! 那么接下来我们用一个实例体会new带来的好处。
struct ListNode
{
int _val;
ListNode* _prev;
ListNode* _next;
ListNode(int val=0)
:_val(val)
,_prev(nullptr)
,_next(nullptr)
{}
};
class List
{ public:
List()
{
_head = new ListNode;
_head->_prev = _head;
_head->_next = _head;
}
~List()
{
ListNode* cur = _head->_next;
while (cur != _head)
{
ListNode* next = cur->_next;
delete cur;
cur = next;
}
delete _head;
_head = nullptr;
}
private:
ListNode* _head;
};
int main()
{
List L;
return 0;
}
我们只要在main里面写这样一个语句就可以创建一个带头双向循环链表了!相比于先前使用C语言写一大堆冗余的函数来进行工作,一个new就为我们不少的问题。 顺便补充一下,使用vs开发的时候,vs会强制性检查malloc,realloc,calloc动态开辟以后返回的指针的合法性,而使用new就不会检查,因为new失败以后不是返回空指针,而是会抛出异常。关于异常,我会在后续的博客内容中介绍,这里只要了解一下new失败是抛出异常就可以了。
2.delete和free的区别 C++也提供了特定的内存释放的方式---->delete,那么首先我们先来看一看delete是怎么使用
int main()
{
int* p=new int;
delete p;
int* pp=new int[10];
delete[] pp;
return 0;
}
注意:new就要和delete配对!malloc,realloc,calloc就要和free一起!new数组就要用delete[]释放 。不要混着乱用!乱用可能有的时候不会出错,但是多数情况下可能会导致程序崩溃! 那么和C++引入new一样,C++引入delete来管理内存的释放肯定是有delete的独到之处的,我们不妨来看一下经典的MyQueue类,看看free和delete一个MyQueue对象是一种什么样的场景:
class Stack
{
public:
Stack(int capacity=4)
:_a(new int[capacity])
,_size(0)
,_capacity(capacity)
{}
~Stack()
{
delete[] _a;
_a = nullptr;
}
private:
int* _a;
size_t _size;
size_t _capacity;
};
class MyQueue
{
private:
Stack _pushst;
Stack _popst;
};
int main()
{ MyQueue* p=new MyQueue;
delete p;
return 0;
}
此时还没执行delete,单步按下F11进入下一步调用的函数内部 可以看到,delete并不是直接就把p指向的那块空间释放了,而是先调用了析构函数清理资源!最后清理完资源以后才会释放p指向的空间!而如果是free,free就只会释放当前指针指向的那块空间,极大可能会造成内存泄漏!
总结
- new不仅会申请空间,还会调用构造函数
- delete在释放指针指向空间之前,会先调用析构函数清理资源。
文章主要内容就到这里,希望大家可以共同进步。
|