C++入门之内存管理

C++入门之内存管理

1. C/C++中程序在内存中的区域划分

  • 栈:又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的(从高地址向低地址增长)
  • 堆:用于程序运行时动态内存分配,堆是可以上增长的(从低地址向高地址增长)
  • 数据段(静态区):存储全局数据和静态数据。
  • 代码段(常量区):可执行的代码/只读常量
    在这里插入图片描述
  1. globalVar 和 staticGlobalVar 是全局变量,staticVar 被 static修饰,是静态变量,全局变量和静态变量位于数据段(静态区)
  2. localVar num 等等都是局部变量,位于栈上
  3. “abcd” 为常量字符串,是不可修改的,只读,位于代码段(常量区)
  4. malloc动态申请的空间,位于堆上

2. C语言中动态内存管理方式:malloc/calloc/realloc/free

  简单回顾以下C语言中动态内存管理的方式

#include <iostream>
using namespace std;

int main()
{
	int* p1 = (int*)malloc(sizeof(int)); //不会初始化
	free(p1);

	int* p2 = (int*)calloc(4,sizeof(int) );//会将数据初始化为0
	int* p3 = (int*)realloc(p3, sizeof(int) * 10);//重新分配内存
	free(p3);

	return 0;
}

区别:malloc不会初始化空间,而calloc会将空间初始化为0,而realloc则是重新分配空间

3. C++中动态内存管理方式:new/delete

  C语言中的方式依旧可以使用,C++中提出了新的方式newdelete操作符进行动态内存管理

  • 对于内置类型
#include <iostream>
using namespace std;

int main()
{
	int* p1 = new int;//开辟一个int的空间
	int* p2 = new int(10);//开辟一共int的空间并初始化为10
	
	int* p3 = new int[10] {1,2,3,4};//开辟10个int的空间,且初始化前4个,剩下的则初始化为0

	delete p1; //释放一个int的空间
	delete p2;
	delete[] p3;//释放多个int的空间
	
	return 0;
}
  • 对于自定义类型
#include <iostream>
using namespace std;

class A
{
public:
	A(int x = 10)
		:_a(x)
	{
		cout << "A(int x = 10)" << endl;
	}
	A(int x, int y)
		:_a(y)
	{
		cout << "A(int x, int y)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;

	}
private:
	int _a;
};

int main()
{
	A* p1 = new A; //调用默认构造
	A* p2 = new A(1); //传值调用默认构造
	A* p3 = new A(1,2); //调用两个参数的构造

	A* p4 = new A[10]{ 1,2,3,{4,5} };//可以调用默认构造和普通构造混着来,剩下的则调用默认构造,共调用10次构造

	//调用析构
	delete p1;
	delete p2;
	delete p3;
	delete[] p4; //调用10次析构
	return 0;
}

与C语言中的最大区别:

  malloc calloc reallocnew delete在处理内置类型时,几乎是一样的,但是在处理自定义类型时,newdelete除了开辟/释放空间,还会调用类的默认构造和析构函数

  • new T [n]
#include <iostream>
using namespace std;

class A
{
public:
	A(int x = 10)
		:_a(x)
	{
		cout << "A(int x = 10)" << endl;
	}
private:
	int _a;
};

int main()
{
	A* p2 = new A[10];  //可能会多开辟4个字节的空间 44 or 40
	delete[] p2;

	return 0;
}

  在上述代码中,有析构函数时,可能会多开辟4个字节的空间,多开辟的4个字节空间用来存储对象的个数,方便在delete[]时,知道有几个对象,需要调用几次析构函数

  • new 和 delet 应当配对使用,否则结果是不确定
#include <iostream>
using namespace std;

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

int main()
{
	int* p1 = new int[10]; //40
	free(p1);//没问题,但是不建议

	A* p2 = new A[10];  //44 or 40
	free(p2);//程序奔溃

	return 0;
}

  由于类中存在析构函数,可能会多开辟4个字节的空间来存储对象的个数,使用delete[ ]时,会从多开辟的位置开始释放,而free不会,free的位置错误,导致程序奔溃

4. operator new与operator delete函数

newdelete是动态内存申请和释放的操作符,而operator new与operator delete是两个全局函数,operator new与operator delete不是重载的newdelete

  • new在底层是通过operator new这个全局函数来申请空间的
  • delete在底层是通过operator delete这个全局函数来释放空间的
//operator new operator delete的源码
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);
}
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;
}

简单来说:

  • operator new实际上是通过malloc来申请空间的,不过malloc失败时是返回的是空指针,且需要检查是否申请空间成功,而operator new失败后则会抛出异常,比返回空指针更直观,operator new相当于是malloc的封装 + 失败抛出异常
  • operator delete实际上是通过free来释放空间的,是与 operator new完成一个配对

5. new和delete的实现原理

  • new

    1. 先调用operator new函数(调用malloc)来申请空间,失败则抛出异常
    2. 再调用类的构造函数,完成对象的构造
  • delete

    1. 先调用类的析构函数,完成类中对象的资源清理
    2. 再调用operator delete函数(调用free)来释放空间
  • new T[N]

    1. 先调用operator new[] 函数(实际调用operator new)来申请空间,完成对N个对象的空间申请(一次完成)
    2. 再在申请的空间上调用N次构造函数
  • delete[]

    1. 先调用N次析构函数,完成N个对象的资源清理
    2. 再调用operator delete[] 函数(实际调用operator delete)来释放空间

6. malloc/free和new/delete的区别

    1. malloc/free是函数,而new/delete是操作符
    1. malloc的返回值为void*,需要强转使用,而new不需要,new后跟的空间的类型
    1. malloc/free开辟的空间不会初始化,而new/delete可以初始化
    1. malloc/free开辟空间时需要手动计算大小, new只需开辟在[]指定数量
    1. malloc开辟的空间失败时,返回一个空指针,因此使用前需要判断是否为空,而new失败后会抛出异常,只需捕获异常
    1. 在处理自定义类型时, malloc/free只会开辟/释放空间,而
      new在开辟空间后,还会调用构造和析构, delete在释放空间前,还会调用析构

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-23 06:50:03       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-23 06:50:03       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-23 06:50:03       45 阅读
  4. Python语言-面向对象

    2024-07-23 06:50:03       55 阅读

热门阅读

  1. 时间和空间复杂度

    2024-07-23 06:50:03       17 阅读
  2. vivado IOBDELAY

    2024-07-23 06:50:03       16 阅读
  3. React/Vue项目解决跨域的方法

    2024-07-23 06:50:03       18 阅读
  4. 第五节shell脚本中的运行流程控制(3.2)

    2024-07-23 06:50:03       15 阅读
  5. 后端存储流程结构的思考

    2024-07-23 06:50:03       12 阅读
  6. 【DevOps系列】构建Devops系统

    2024-07-23 06:50:03       16 阅读
  7. 寻访安康茶韵,共筑乡村振兴

    2024-07-23 06:50:03       15 阅读