C++动态内存管理

目录

  • C语言中的动态内存管理
  • C++动态内存管理
    • 动态管理内置类型
    • 动态管理自定义类型
  • new和delete的实现原理
    • operator new和operator delete函数
    • new和delete对内置类型的实现原理
    • new和delete对自定义类型的实现原理
  • malloc/free和new/delete区别

C语言中的动态内存管理

之前学习了C语言中的动态内存管理:C语言中动态内存管理

我们这里简单复习一下:
C语言中动态内存管理的函数:malloc,calloc,realloc,free

int main()
{
	int* p1 = (int*)malloc(sizeof(int) * 4);

	int* p2 = (int*)calloc(4, sizeof(int));

	int* p3 = (int*)realloc(p1, sizeof(int) * 10);

	free(p2);
	free(p3);
}

这里我们可以看出,C语言中开辟动态资源,需要类型强转,需要写入开辟具体的字节数
其实还不是很方便

所以C++内存管理解决了这个问题

C++动态内存管理

C++兼容C,所以malloc,calloc,realloc,free在C++中可以继续使用
C++提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理

动态管理内置类型

  • 动态申请一个int类型空间:
int* p1 = new int;

这里可以看出C++中开辟一个int类型,就直接在new后面写上int类型就可以,在C中,我们需要写入Int类型的大小

这里没有进行初始化,所以开辟出的空间里的是随机值

  • 动态申请一个int类型空间,并且初始化为3:
int* p2 = new int(3);

C++中,new的同时是可以初始化的,这是C语言中动态内存开辟中不具备的能力

  • 动态申请3个int类型空间:
int* p3 = new int[3];

这里没有进行初始化,所以开辟出的空间里的是随机值

  • 动态申请5个int类型空间,并且初始化:
int* p4 = new int[5] {1, 2, 3, 4, 5};
  • 释放空间
delete p1;
delete p2;
delete[] p3;
delete[] p4;

申请和释放单个元素的空间,使用newdelete操作符
申请和释放连续的空间,使用new[]delete[]操作符

newdelete配合,mallocfree配合使用,不要交叉混合使用


动态管理自定义类型

在申请自定义类型空间时,new会自动调用构造函数,delete会自动调用析构函数,而mallocfree不会
这也是new/delete和malloc/free最大的区别

现在有如下的类:

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a = 0)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};

用malloc和free管理一个自定义对象

int main()
{
	A* pa1 = (A*)malloc(sizeof(A));
	free(pa1);
	return 0;
}

可以看到没有任何输出,也就是不会自动调用构造函数和析构函数
C++动态内存管理_第1张图片
接着用new/delete管理一个自定义对象

int main()
{
	A* pa1 = (A*)malloc(sizeof(A));
	free(pa1);

	A* pa2 = new A;
	delete pa2;
	return 0;
}

可以看到,结果有输出内容,也就说明new可以调用构造函数,delete会调用析构函数
C++动态内存管理_第2张图片
因为有默认构造函数,所以A* pa2 = new A;这里自动按照默认构造函数赋值
在这里插入图片描述
如果没有默认构造函数,A* pa2 = new A;这样的写法会报错的
C++动态内存管理_第3张图片

这时就需要传值

A* pa2 = new A(1);

开辟连续空间的自定义对象:

A* pa3 = new A[3]{ 1,2,3 };

上面这种写法会涉及到隐式类型转换,把int类型转成自定义类型

也可以使用匿名对象来进行初始化:

A* pa3 = new A[3]{ A(1),A(2),A(3) };

对于A* pa3 = new A[3]{ A(1),A(2),A(3) };这样开辟连续空间时,也会多次调用构造函数
同理delete[] pa3也会多次调用析构函数

C++动态内存管理_第4张图片


new和delete的实现原理

operator new和operator delete函数

new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间

面向对象的语言处理失败,不喜欢用返回值,喜欢用抛出异常
所以malloc申请空间失败返回NULL,而new申请失败是抛出异常

operator new 对malloc进行封装,如果失败抛异常
所以operator new不是直接给我们用的,而是给new用的

同理operator delete是对free函数进行封装,同时如果失败抛出异常,operator delete 不是给我们用的,而是给delete用的

下面是operator new和operator delete函数的源码,的确可以看到对malloc和free的封装,以及检测异常的部分:

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
    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;
}

new和delete对内置类型的实现原理

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

new和delete对自定义类型的实现原理

C++动态内存管理_第5张图片
new的原理:先调用operator new函数申请空间,再在申请的空间上执行构造函数,完成对对象的初始化
delete的原理:先调用析构函数,完成对对象中资源的清理工作,再调用operator delete函数

这里需要注意的是:new是先开空间再构造,而delete是先析构再释放空间,不要搞混

下面我们通过一个Stack类能够更好的理解:

class Stack
{
public:
	Stack(int capacity = 4, int top = 0)
		:_capacity(capacity)
		, _top(top)
	{
		_a = new int[capacity];
	}
	~Stack()
	{
		delete[] _a;
	}

private:
	int* _a;
	int _capacity;
	int _top;
};

在运行Stack* pst = new Stack语句时,只能先开辟一段空间,然后再调用构造函数malloc出一段空间给_a
C++动态内存管理_第6张图片

接着在运行delete[] pst时,必须先调用析构函数。这里如果先释放空间,则是先释放在堆区中对象的空间,那么_a指向的空间没有释放,这里造成内存泄露
C++动态内存管理_第7张图片
所以必须先用析构,将数组空间释放,再释放对象整体

其实除了operator new和 operator delete外,还有operator new[]和operator delete[]函数,这两个是用来开辟和释放连续空间的对象
new T[N]:调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的开辟,然后再执行N次构造函数
delete[]:先调用N次析构函数,完成对象中资源的清理,再调用operator delete[]释放空间,operator delete[]中是调用N次operator delete来释放空间


malloc/free和new/delete区别

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请空间不可以初始化,new可以
  3. malloc返回值类型是void*,接收时需要强转,new不需要
  4. malloc申请空间时,需要手动计算开辟空间的大小,而new不需要,后面跟上空间类型就可以。多个对象时,在[]中指定对象个数即可,不用手动计算大小
  5. malloc失败后返回NULL,而new失败后会抛出异常
  6. 申请自定义类型对象时,malloc和free只会开辟和释放空间,不会调用构造和析构函数,而new/delete,申请空间后会调用构造函数,释放空间前会调用析构函数。

你可能感兴趣的:(C++,c++,开发语言,对象)