C++内存管理
Posted 正义的伙伴啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++内存管理相关的知识,希望对你有一定的参考价值。
文章目录
C++内存管理
C++内存分布(四大内存分区)
下面依次简单的介绍一下各个分区:
- 栈:主要存储函数参数,局部变量,栈区的大小实际上大概只有8M
- 堆:存储动态开辟的变量,堆的大小比栈大多了,所以一些较大的数据一般都存在堆上面
- 数据段:存储全局变量,静态变量
- 代码段: 存储可执行代码、只读常量(例如字符常量)
int main()
const char* a = "abc";
char b[] = "abc";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
free(ptr1);
// a在哪里: 栈区
// *a在哪里: 代码段
// b在哪里:栈区
// *b在哪里:栈区
// ptr1在哪里: 栈区
// *ptr1在哪里:堆区
C++中内存管理的方式
C++用关键字:new 和 delete来申请和释放空间
new初始化:
int* ptr = new int; //直接开辟一块空间
int* ptr1 = new int(5); //初始化的时候直接赋值
int* ptr2 = new int[10];//开辟十个连续空间(这要求int有默认构造函数)
这就是new的三种定义方式
delete 用法:
delete ptr;
delete ptr1;//回收一段空间
delete[] ptr2;//回收一段连续的空间,相当于连续调用delete
但是这里new、delete 和 malloc、free好像并没有什么区别?
- 对于内置类型:两者没有什么区别
- 如果是自定义类型,new、delete会调用类的构造和析构函数申请和释放空间,但是malloc、free不会
operator new和operator delete
operator new和operator delete是一个全局函数,而new和delete是关键字,注意两者本质上的区别!
这是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);
operator delete的源代码:
_free_dbg( pUserData, pHead->nBlockUse );
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
我们发现operator new 和 operator delete其实底层都是调用malloc和free来申请和释放空间的
operator new、operator delete与malloc和free的区别是什么?
区别在于处理错误的方式不同:malloc开辟失败返回的是空指针,但是operator new开辟空间失败是抛出异常,我们可以理解成
operator new=malloc+失败时抛出异常
operator delete=free + 失败时抛出异常
new、delete和operator new、operator delete有什么区别?
new、delete会调用类的构造和析构函数申请和释放空间,但是operator new 、operator delete不会,实际上:
new=operator new+构造函数
delete=析构函数 + operator delete
重载operator new、operator delete
上面讲了operator new、operator delete是一个全局的函数,我们还可以对operator new 和 operator delete在类里面进行重载
以达到自己的一些需求,operator new和operator delete有两个重载版本:
void* operator new (size_t t);
void* operator new[] (size_t t);
void operator delete (void* p);
void operator delete[] (void * p);
这是在类里面的重载时的类型,然后我们在这些重载类型中加入一些自己想实现的功能,我们在外面调用时按照new、delete的写法去调用,这时会编译器会调用重载的operator new或operator delete 和 构造函数 或 析构函数
调用顺序的确定
class A
public:
static int _c;
A(int x=0)
:_b(x)
cout << "构造函数" << endl;
_a = new int[5];
void* operator new(size_t t)
cout << "调用 operator new" << endl;
A* p = (A*)::operator new(t);
_c++;
return p;
void operator delete(void* p)
cout << "调用 operator delete" << endl;
::operator delete(p);
_c--;
~A()
cout << "析构函数" << endl;
free(_a);
_a = NULL;
private:
int* _a;
int _b;
;
int A::_c = 0;
int main()
A* p = new A(1);
delete p;
这里就可以知道:在A* p = new A(1);
时底层是先调用operator new再调用构造函数,而在delete p;
时正好反过来,先调用析构函数再调用operator delete。
应用:检查空间是否完全释放
class A
public:
static int _c;
A(int x=0)
:_b(x)
//cout << "构造函数" << endl;
_a = new int[5];
void* operator new(size_t t)
//cout << "调用 operator new" << endl;
A* p = (A*)::operator new(t);
_c++;
return p;
void operator delete(void* p)
//cout << "调用 operator delete" << endl;
::operator delete(p);
_c--;
~A()
//cout << "析构函数" << endl;
free(_a);
_a = NULL;
private:
int* _a;
int _b;
;
int A::_c = 0; //定义一个静态变量,如果调用构造函数就++,没调用就--,如果程序结束时_c为0,则说明内存全部释放
int main()
A* ps1 = new A(1);
A* ps2 = new A(2);
A* ps3 = new A(3);
A* ps4 = new A(4);
A* ps5 = new A(5);
A* ps6 = new A(6);
A* ps7 = new A(7);
delete ps1;
delete ps2;
delete ps3;
delete ps4;
delete ps5;
delete ps6;
cout << A::_c << endl;
这里就可以看到ps7没有释放,所以最后结果不为0;
总结:
我的理解是:operator new、operator delete(全局) 给内存申请、释放提供了一个自定义的方法,我们可以在类里面自定义operator new、operator delete来实现一些别的功能,其他好像也并不是很有用
定位new表达式
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象
因为类的初始化一定是伴随着类的定义,所以就无法给已经初始化好的对象赋值,placement new正好完成了这个任务
使用格式:new(place_address) type;
或 new(place_address) type(initializer_list);
place_address 必须是一个指针,type是初始化变量的类型,initializer_list是类型的初始化列表
int* p = new int[10];//这里用new[]开辟的空间并不能在开空间的时候赋初值
for (int i = 0; i < 10; i++)
new(p+i) int(5);
for (int i = 0; i < 10; i++)
cout << p[i] << " ";
内存泄漏
先看一个问题:
class A
public:
static int _c;
A(int x=0)
:_b(x)
//cout << "构造函数" << endl;
_a = new int[5];
void* operator new(size_t t)
//cout << "调用 operator new" << endl;
A* p = (A*)::operator new(t);
_c++;
return p;
void operator delete(void* p)
//cout << "调用 operator delete" << endl;
::operator delete(p);
_c--;
~A()
//cout << "析构函数" << endl;
free(_a);
_a = NULL;
private:
int* _a;
int _b;
;
int A::_c = 0;
int main()
A a;
A* p = new A;
这里我们要搞清楚 类a 和 类a的成员变量 以及 指针p 、指针p所指向的类 、指针p所指向的类的成员变量 所在的空间
弄清楚这个问题会对delete有更深的理解:为什么要先调用析构函数,在销毁空间!
在析构的时候:调用析构函数先把 蓝色_a所指向的空间 释放掉,在销毁p在堆上的空间,如果先销毁p在堆上的空间, 蓝色_a所指向的空间 就无法释放造成内存泄漏。
上面的问题提到的内存泄漏,是一个很头疼的问题
如果像 蓝色_a所指向的空间(泛指各种不规范操作造成的内存无法释放)积累到一定程度,就会造成堆上无法在申请到空间。
但是有些时候我们发现一些内存泄漏在程序结束之后就会“自动消失”,这是因为一个进程正常结束后,内存就会被释放掉,但是设想在一些24h运行的服务器上进程不会结束,这样内存泄漏的危害就会很大了
malloc/free 和 new/delete区别的总结
共同点:都是手动从堆上面开辟空间
不同点:
- malloc和free 是函数,而new和delete是操作符
- malloc申请空间时需要手动计算大小并传递,而new只需要输入类型和个数即可
- malloc的返回值是void *,使用前必须强转,new不需要,因为new后面跟的就是类型
- malloc申请失败会返回空指针,因此使用前必须要判空,new申请失败会抛出异常
- 申请自定义对象时:malloc和free只负责开辟空间,不会调用构造或析构函数,而new申请空间会先开辟空间再调用构造函数,delete会先调用析构函数再释放空间
以上是关于C++内存管理的主要内容,如果未能解决你的问题,请参考以下文章