【C++】C++内存管理分布

 

目录

思维导图大纲:

1. 内存底层分别 

分析答案: 

 2. 内存管理

c语言中的内存管理: 

c++中的内存管理: 

创建(单对象/多对象/)+ 初始化 :

创建一个链表: 

 失败抛异常:

正确配对使用new/delete: 

3. operator new与operator delete函数 

4. 定位new表达式(placement-new) 

5. new和delete的实现原理 

内置类型: 

自定义类型: 

malloc/free和new/delete的区别 


思维导图大纲:

1. 内存底层分别 

 

 C/C++内存管理
 我们知道在内存区大致分为以下几块:栈区,堆区,静态区(数据段),常量区(代码段)

 我们来分析以下代码,看看他们分别处于什么区域
    globalVar在哪里?____
    staticGlobalVar在哪里?____
    staticVar在哪里?____
    localVar在哪里?____
    num1 在哪里?____
    char2在哪里?____
    *char2在哪里?___
    pChar3在哪里?____
    *pChar3在哪里?____
    ptr1在哪里?____
    *ptr1在哪里?____    


int globalVar = 1;


static int staticGlobalVar = 1;

void Test()
{
	
	static int staticVar = 1;

	
	int localVar = 1;

	
	int num1[10] = { 1, 2, 3, 4 };

	
	char char2[] = "abcd";

	
	const char* pChar3 = "abcd";

	
	int* ptr1 = (int*)malloc(sizeof(int) * 4);
	int* ptr2 = (int*)calloc(4, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);

	free(ptr1);
	free(ptr3);
}

分析答案: 

 

// globalVar是全局变量,属于静态区(数据段)
int globalVar = 1;

// staticGlobalVar是静态变量,属于静态区(数据段)
static int staticGlobalVar = 1;

void Test()
{
	// staticVar是Test函数栈帧空间上的静态变量,属于静态区(数据段)
	static int staticVar = 1;

	// localVar是正常变量,在栈上
	int localVar = 1;

	// num1是数组的名字,数组名在此处代表首元素地址在栈上
	int num1[10] = { 1, 2, 3, 4 };

	// char2是数组的名字,数组名在此处代表首元素地址在栈上
	// *char2表示首元素'a',处于栈上
	char char2[] = "abcd";

	// const修饰char* pChar3
	// pChar3在栈上
	// *pChar3由于const的修饰在常量区(代码段)
	const char* pChar3 = "abcd";

	// 以下的ptr(1~3)属于一个名称,在栈上
	// *ptr(1~3)访问的是申请的空间在堆区
	int* ptr1 = (int*)malloc(sizeof(int) * 4);
	int* ptr2 = (int*)calloc(4, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);

	free(ptr1);
	free(ptr3);
}

 2. 内存管理

c语言中的内存管理: 

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

void Test()
{
	// 1.malloc/calloc/realloc的区别是什么?
	// 三者都是可以开空间
	// malloc:开空间不初始化
	// calloc:开空间可以初始化
	// realloc:可以在原有的空间上进行增容扩容
	int* p1 = (int*)calloc(4, sizeof(int));
	int* p2 = (int*)realloc(p1, sizeof(int) * 10);
	// 这里需要free(p2)吗?
	//free(p2);
	// 不需要,如果这块空间足够,realloc会直接向后开辟申请空间,不需要释放空间
	// 如果这块空间不够,realloc会申请另一块新的空间,将原空间内部资源拷贝过去然后释放,
	// 不需要我们手动释放空间
} 

c++中的内存管理: 

创建(单对象/多对象/)+ 初始化 :

// c++中申请内存使用new/delete
// 不同于c语言中的malloc/realloc/free等等,new/delete使用会去调用构造函数和析构函数

class A
{
public:
	// 构造
	A(int a1 = 10, int a2 = 20)
		:_a1(a1)
		, _a2(a2)
	{
		cout << "A(int a1 = 10, int a2 = 20)" << endl;
	}

	// 拷贝构造
	A(const A& a)
	{
		cout << "A(const A& a)" << endl;
		_a1 = a._a1;
		_a2 = a._a2;
	}

	// 析构
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	// 声明成员变量,给缺省值用于初始化列表
	int _a1 = 10;
	int _a2 = 20;
};

int main()
{
	// new申请一个对象
	A* p1 = new A;
	delete p1;

	// new申请多个对象
	A* p2 = new A[4];
	delete[] p2;

	// new申请对象+初始化
	// 方法一:
	// 构造+拷贝
	A aa1(1, 1);
	A aa2(2, 2);
	A aa3(3, 3);
	A aa4(4, 4);
	A* p3 = new A[4]{aa1, aa2, aa3, aa4};
	delete[] p3;

	// 方法二:类型转换
	// 优化后:构造
	A* p4 = new A[4]{ {1,1},{2,2},{3,3},{4,4} };
	delete[] p4;

	// 方法三:匿名对象
	// 构造
	A* p5 = new A[4]{ A(1,1), A(2,2), A(3,3), A(4,4) };
	delete[] p5;

	return 0;
}

 

 

创建一个链表: 

// 使用new来创建一个链表
class ListNode
{
public:
	ListNode(int val = 0)
		:_val(val)
		,_next(nullptr)
	{}

	int _val = 0;
	ListNode* _next;
};

int main()
{
	ListNode* n1 = new ListNode(1);
	ListNode* n2 = new ListNode(2);
	ListNode* n3 = new ListNode(3);
	ListNode* n4 = new ListNode(4);
	n1->_next = n2;
	n2->_next = n3;
	n3->_next = n4;

	delete n1;
	delete n2;
	delete n3;
	delete n4;

	return 0;
}

 失败抛异常:

// new申请失败不会返回NULL,而是抛异常

void Text01()
{
	// 关键字 throw/try/catch
	void* p1 = new char[1024 * 1024 * 1024];  // 1G

	cout << p1 << endl;
	void* p2 = new char[1024 * 1024 * 1024];  // 1G
	cout << p2 << endl;

	void* p3 = new char[1024 * 1024 * 1024];  // 1G
	cout << p3 << endl;

}

void Text02()
{
	int n = 0;
	while (1)
	{
		void* p1 = new char[1024 * 1024];  // 1M
		++n;
		cout << p1 << "->" << n << endl;
	}
	
}
int main()
{
	try
	{
		// Text01();
		// 虚拟内存
		// 32位 -> 4G  
		// 64为 -> 8G
		// Text02();
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}

	return 0;
}

正确配对使用new/delete: 

// 正确使用new和delete
class A
{
public:
	A(int a1 = 1, int a2 = 2)
		:_a1(a1)
		,_a2(a2)
	{}

private:
	int _a1 = 1;
	int _a2 = 2;
};

class B
{
public:
	B(int b1 = 1, int b2 = 2)
		:_b1(b1)
		, _b2(b2)
	{}

private:
	int _b1 = 1;
	int _b2 = 2;
};

int main()
{
	// new 与 delete 对应
	// new[] 与 delete[] 对应
	// 串用可能会出现以下情况

	// 可能会出现报错
	A* p1 = new A[10];
	delete p1;

	B* p2 = new B[20];
	delete p2;

	return 0;
}

3. operator new与operator delete函数 

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

class A
{
public:
	A(int a = 1)
		:_a(a)
	{}
	~A()
	{}
	void Print() const
	{
		cout << _a << endl;
	}
private:
	int _a = 1;
};
int main()
{
	// 写法一:
	A* ptr1 = new A(1);
	ptr1->Print();

	// 写法二:
	// 申请空间+初始化
	A* ptr2 = (A*)operator new(sizeof(A));
	new(ptr2)A(2);
	ptr2->Print();
	// 析构+释放空间
	ptr2->~A();
	operator delete(ptr2);

	return 0;
}

4. 定位new表达式(placement-new) 

这种方法看起来有点多此一举的味道,明明直接使用new/delete就可以直接申请
但是在某些场景下:如内存池,我们默认申请的空间不去堆而是去内存池,就需要new表达式(placement-new) 

class A
{
public:
	A(int a = 1)
		:_a(a)
	{}
	~A()
	{}
private:
	int _a = 1;
};
int main()
{
	A* ptr1 = (A*)malloc(sizeof(A));
	new(ptr1)A(1);
	ptr1->~A();
	free(ptr1);

	A* ptr2 = (A*)operator new(sizeof(A));
	new(ptr2)A(2);
	ptr2->~A();
	operator delete(ptr2);

	return 0;
}

 我们有时存在一块高频繁调用的内存块,为了更加高效我们手动从堆中申请一块内存池子只用于我们申请,这时候我们就需要使用前面的方法了不去调用operator new,而是其他的容器,因为operator new默认向堆中申请空间

5. new和delete的实现原理
 

内置类型: 

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

自定义类型: 

  • new的原理

1. 调用operator new函数申请空间

2. 在申请的空间上执行构造函数,完成对象的构造

  • delete的原理

1. 在空间上执行析构函数,完成对象中资源的清理工作

2. 调用operator delete函数释放对象的空间

  •  new T[N]的原理

1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请

2. 在申请的空间上执行N次构造函数

  • delete[]的原理

1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理

2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

malloc/free和new/delete的区别
 

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:

1. malloc和free是函数,new和delete是操作符

2. malloc申请的空间不会初始化,new可以初始化

3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可

4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型

5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常

6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放

 

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值