首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【C++】内存管理

【C++】内存管理

作者头像
啊QQQQQ
发布2024-11-19 19:09:41
发布2024-11-19 19:09:41
13900
代码可运行
举报
文章被收录于专栏:C++C++
运行总次数:0
代码可运行

C/C++内存分布

我们先来看下面一段代码

代码语言:javascript
代码运行次数:0
运行
复制
    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);
    }
    1. 选择题:
        选项 : A.栈  B.堆  C.数据段(静态区)  D.代码段(常量区)
        globalVar在哪里?____   staticGlobalVar在哪里?____
        staticVar在哪里?____   localVar在哪里?____
        num1 在哪里?____

        char2在哪里?____ * char2在哪里?___
        pChar3在哪里?____ * pChar3在哪里?____
        ptr1在哪里?____ * ptr1在哪里?____
        2. 填空题:
        sizeof(num1) = ____;
        sizeof(char2) = ____;      strlen(char2) = ____;
       sizeof(pChar3) = ____;     strlen(pChar3) = ____;
       sizeof(ptr1) = ____;

【说明】

1. 又叫堆栈 -- 非静态局部变量 / 函数参数 / 返回值等等,栈是向下增长的。

2. 用于程序运行时动态内存分配,堆是可以上增长的。

3. 数据段 -- 存储全局数据和静态数据。

4. 代码段 -- 可执行的代码 / 只读常量。

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

代码语言:javascript
代码运行次数:0
运行
复制
void Test ()
{
int* p1 = (int*) malloc(sizeof(int));
free(p1);
// 1.malloc/calloc/realloc的区别是什么?
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 这里需要free(p2)吗?
free(p3 );
}

C++中动态内存管理

C 语言内存管理方式在 C++ 中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因 此 C++ 又提出了自己的内存管理方式: 通过 new delete 操作符进行动态内存管理

new/delete的操作内置类型

代码语言:javascript
代码运行次数:0
运行
复制
void Test()
{
	// 动态申请一个int类型的空间
	int* ptr4 = new int;

	// 动态申请一个int类型的空间并初始化为10
	int* ptr5 = new int(10);

	// 动态申请10个int类型的空间
	int* ptr6 = new int[3];
	delete ptr4;
	delete ptr5;
	delete[] ptr6;
}

注意:申请和释放单个元素的空间,使用 new delete 操作符,申请和释放连续的空间,使用

new[] delete[] ,注意:匹配起来使用。

new/delete的操作自定义类型

代码语言:javascript
代码运行次数:0
运行
复制
class A
{
public:
		A(int a = 0)           
		: _a(a)
	{
		;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
int main()
{
	// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间
	//还会调用构造函数和析构函数
		A* p1 = (A*)malloc(sizeof(A));
	A* p2 = new A(1);
	free(p1);
	delete p2;
	// 内置类型是几乎是一样的
	int* p3 = (int*)malloc(sizeof(int)); // C
	int* p4 = new int;
	free(p3);
	delete p4;
	A* p5 = (A*)malloc(sizeof(A) * 10);
	A* p6 = new A[10];
	free(p5);
	delete[] p6;
	return 0;
}

注意:在申请自定义类型的空间时, new 会调用构造函数, delete 会调用析构函数,而 malloc

free 不会

operator new与operator delete函数

new delete 是用户进行 动态内存申请和释放的操作符operator new operator delete

系统提供的 全局函数new 在底层调用 operator new 全局函数来申请空间, delete 在底层通过

operator delete 全局函数来释放空间。

operator new 实际也是通过 malloc 来申请空间 ,如果

malloc 申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施

就继续申请,否则就抛异常。 operator delete 最终是通过 free 来释放空间的

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 来释

放空间

定位new表达式(placement-new)

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

使用格式:

new (place_address) type 或者 new (place_address) type(initializer-list)

place_address 必须是一个指针, initializer-list 是类型的初始化列表

使用场景:

定位 new 表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如

果是自定义类型的对象,需要使用 new 的定义表达式进行显示调构造函数进行初始化

代码语言:javascript
代码运行次数:0
运行
复制
class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
// 定位new/replacement new
int main()
{
	// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没
	//有执行
		A* p1 = (A*)malloc(sizeof(A));  
	new(p1)A;  
	p1->~A();                
	free(p1);  
	A* p2 = (A*)operator new(sizeof(A));  
	new(p2)A(10);  
	p2->~A();  
	operator delete(p2);  
	return 0;
}

内存泄漏

什么是内存泄漏,内存泄漏的危害

什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内 存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对 该段内存的控制,因而造成了内存的浪费。 内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现 内存泄漏会导致响应越来越慢,最终卡死。

代码语言:javascript
代码运行次数:0
运行
复制
void MemoryLeaks()
{
   // 1.内存申请了忘记释放
  int* p1 = (int*)malloc(sizeof(int));
  int* p2 = new int;
  
  // 2.异常安全问题
  int* p3 = new int[10];
  
  Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
  
  delete[] p3;
}

内存泄漏的分配

C/C++ 程序中一般我们关心两种方面的内存泄漏:

堆内存泄漏 (Heap leak)

堆内存指的是程序执行中依据须要分配通过 malloc / calloc / realloc / new 等从堆中分配的一

块内存,用完后必须通过调用相应的 free 或者 delete 删掉。假设程序的设计错误导致这部分

内存没有被释放,那么以后这部分空间将无法再被使用,就会产生 Heap Leak 。

系统资源泄漏

指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放

掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

因此写代码时一定要小心,尤其是动态内存操作时,一定要记着释放。但有些情况下总是防不胜

防,简单的可以采用上述方式快速定位下。如果工程比较大,内存泄漏位置比较多,不太好查时

一般都是借助第三方内存泄漏检测工具处理的。

如何避免内存泄漏

1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。 ps :

这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智

能指针来管理才有保证。

2. 采用 RAII 思想或者智能指针来管理资源。

3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。

4. 出问题了使用内存泄漏工具检测。 ps :不过很多工具都不够靠谱,或者收费昂贵。

总结一下 :

内存泄漏非常常见,解决方案分为两种: 1 、事前预防型。如智能指针等。 2 、事后查错型。如泄

漏检测工具。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-10-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C/C++内存分布
  • C语言中动态内存管理方式
  • C++中动态内存管理
    • new/delete的操作内置类型
    • new/delete的操作自定义类型
  • operator new与operator delete函数
  • new和delete的实现原理
    • 内置类型
    • 自定义类型
  • 定位new表达式(placement-new)
  • 内存泄漏
    • 什么是内存泄漏,内存泄漏的危害
    • 内存泄漏的分配
    • 如何避免内存泄漏
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档