C++内存管理

C/C++编译的程序占用内存主要分为以下几个部分:

  • 数据段:存放全局变量和静态变量,程序运行时分配,程序结束时释放;
  • 代码段:存放函数体和只读常量,程序运行时分配,程序结束时释放;
  • 堆:由程序猿主动分配与释放,若程序猿没有主动释放则程序结束时自动释放;
  • 栈:存放为运行函数时分配的局部变量、函数参数、返回数据、返回地址等,由编译器自动分配与释放,函数运行开始时分配、结束时释放;

内存分布简易图:

image-20221024134703784

C语言动态内存的管理方式

使用malloc、realloc、calloc来申请堆上的内存,使用free释放堆上的内存。

之前的博客总结过C语言动态内存管理的方式,下面是链接。

C语言动态内存管理,我该如何学习

C++动态内存的管理方式

使用操作符new来申请内存,使用操作符delete来释放内存。

操作内置类型

申请单个空间

//new是操作符,并不是函数,所以new申请空间时,后面不需要加()
int* pi = new int;//申请一块四字节空间,返回int*指针
char* pc = new char;//申请一块一字节空间,返回char*指针

申请单个空间时初始化

//在类型后面加()表示初始化,里面填写初始化的内容
int* pi = new int(10);//申请一块四字节空间,并初始化为10,返回int*指针
char* pc = new char('a');//申请一块一字节空间,并初始化为a,返回char*指针

释放单个空间

delete pi;//释放pi所指空间
delete pc;//释放pc所指空间

申请多个连续空间

//用[]来表示申请多个连续空间,里面填写空间个数
int* pni = new int[10];//申请十块连续的四字节空间,返回第一块的地址为int*指针
char* pnc = new char[5];//申请五块连续的一字节空间,返回第一块的地址为char*指针

申请多个连续空间并初始化

//用{}来表示初始化多个连续空间,若是不完全初始化,则未初始化部分默认为0
int* pni = new int[10]{0,1,2,3,4,5,6,7,8,9};
char* pnc = new char[5]{'a','b','c'};

释放多个连续空间

delete[] pni;//释放pni所指的连续空间
delete[] pnc;//释放pnc所指的连续空间

操作自定义类型

操作方法跟操作内置类型一模一样,只是对自定义类型来说,new会自动调用构造、delete会自动调用析构。

image-20221024145145230

new失败时会抛异常,而不是返回nullptr(malloc失败会返回nullptr)

image-20221024151451638

operator new和operator delete

operator new 和 operator delete是两个函数,而不是运算符重载(本身new和delete就不是运算符)!!

new和delete是C++引入的操作符,operator new和operator delete是系统提供的全局函数。

new在底层调用operator new来申请资源,operator new实际也是在用malloc申请资源;

delete在底层调用operator delete来释放资源,operator delete实际也是在用free释放资源。

//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
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)

new和delete的实现原理

对于内置类型

new、delete和malloc、free大致相似,但有一些不同点:

  • new和delete申请、释放单个空间,new[]和delete[]申请、释放多个连续空间;
  • new和new[]在申请空间失败时会抛异常而不是返回nullptr;

对于自定义类型

new会自动调用构造,delete会自动调用析构

大致流程如下:

new:先调用operator new申请内存,然后再调用构造函数;

delete:先调用析构函数,然后再调用operator delete释放内存;

new[]:先调用operator new[]申请内存,operator new[]会根据次数做标记,然后根据标记调用operator new直接连续开够,再根据标记连续调用构造函数;

delete[]:根据标记连续调用析构函数,然后根据标记调用operator delete释放内存;

定位new表达式

定位new表达式是在以分配的原始内存空间中调用构造函数初始化一个对象

格式:

new(place_address)type或者new(place_address)type(initializer_list)

place_addreee必须是一个指针,initializer_list是类型的初始化列表

使用场景:

定位new表达式一般搭配内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定位表达式显示调构造函数进行初始化。

#include <iostream>
using namespace std;
class A{
public:
    A(int a = 0)
            : _a(a)
    {
        cout << "A():" << this << endl;
    }
    ~A()
    {
        cout << "~A():" << this << endl;
    }
private:
    int _a;
};

int main() {
    A* p1 = (A*)malloc(sizeof(A));//p1所指内存无初始化
    new(p1)A;//定位new表达式,这里没有传初始化参数
    delete p1;

    A* p2 = (A*)malloc(sizeof(A));//p2所指内存无初始化
    new(p2)A(10);//定位new表达式,这里传了初始化参数
    delete p2;

    return 0;
}

malloc、free与new、delete的联系与区别

共同点:都需要手动申请内存(在堆上),并且需要手动释放内存;

不同点:

  • malloc和free是函数。new和delete是操作符;
  • malloc申请的空间不会初始化。new申请的空间会初始化(对内置不做处理,对自定义调用构造);
  • malloc申请空间时需要手动计算大小并传递。new只需要在后面跟上类型,若是多个对象只需要在[]中指定数量;
  • malloc的返回值为void*,必须进行强制类型转换。new不需要,因为new后跟的是空间的类型;
  • malloc申请失败时会返回nullptr,因此使用时必须进行判断。new申请失败会抛异常;
  • 申请自定义类型空间时,new和delete会自动调用构造和析构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云朵c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值