😎【博客主页你最爱的小傻瓜😜】😎

🤔【本文内容:C++内存管理 😍   】🤔

---------------------------------------------------------------------------------------------------------------------------------

内存如人生旷野,new是主动奔赴的相遇,delete是坦然放下的告别。在分配与释放间游走,领悟代码里藏着的,关于拥有与释怀的哲学 。接下来的是21世纪最为震惊的解说,带领小伙伴们走进内存里的世界。好好瞧,不要眨眼!!!
---------------------------------------------------------------------------------------------------------------------------------

迈出的第一步:内存的分布

​  :欸嘿!!!,要长脑子了。博主博主什么是内存分布呀?

​:咳咳。傻孩子,在我们这里讲解,内存是分布在用户空间的,里面可以细分:栈,内存映射段堆,数据段,代码段。

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

内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。

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

数据段--存储全局数据和静态数据。(被static修饰的变量,在函数外)

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

sizeof strlen 区别:

 sizeof:运算符,编译时算类型 / 变量占字节数。含\0等额外空间(如char arr[]="abc",结果 4 )

 strlen:库函数,运行时算字符串(\0前)长度  。仅有效字符数,不含\0(上例结果 3 )。《下面我们来做一些小练习》 。                                                                       

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 在哪__ _ (C)staticGlobalVar 在哪_ __(C)
staticVar 在哪里? ____ (C)localVar 在哪里? ____(A)
num1 在哪里? ____(A)
char2 在哪里? ____ (A) * char2 在哪里? ___(A)
pChar3 在哪里? ____ (A) * pChar3 在哪里? ____(D)
ptr1 在哪里? ____ (A) * ptr1 在哪里? ____(B)
2. 填空题:
sizeof ( num1 ) = ____(40) ;
sizeof ( char2 ) = ____(5) ; strlen ( char2 ) = ____(4) ;
sizeof ( pChar3 ) = ____(8) ; strlen ( pChar3 ) = ____(4) ;
sizeof ( ptr1 ) = ____(8) ;

​      :哈哈哈哈额(转音),博主博主,第一题从左到右,从还是那个往下是C,C,C,A,A ,A, A, A,D,A,B。第二题:还是一样的顺序:40,5,4,8,4,8.(按64位系统)。对是不对!!!

成为内存管理大师的第二布:内存管理方式

  :给兄弟们滴个。

C语言中动态内存管理方式:malloc/calloc/realloc/free:看完一遍,赛过神仙

  :博主博主,你不讲C++里面的吗,怎么讲上C语言上的了。

  :小嘴巴,我们学习的C++的思路就是改善C语言。C++是为了比C语言的使用方式优,我们先了解C语言里头的才能更好的了解C++里头的?OK了,老铁,接下来就是我们期待的C++里头的内存管理方式了。

        new/delete操作内置类型

void Test()
{
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[10];
delete ptr4;
delete ptr5;
delete[] ptr6;
}

        new一个,我们有很多种情况的形式:  

申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和
delete[],注意:匹配起来使用。

     newdelete操作自定义类型

class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~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 不会 。、、、 是不是感觉C++的内存申请空间方式简单多了。

        operator newoperator delete函数(重要点进行讲解)、

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,
尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
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: 该函数最终是通过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;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

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
2 自定义类型
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的定义表达式进行显示调构造函数进行初始化。
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; // 注意:如果A类的构造函数有参数时,此处需要传参
p1->~A();
free(p1);
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(10);
p2->~A();
operator delete(p2);
return 0;
}

malloc/freenew/delete的区别

对比维度 malloc/free new/delete
语法本质 C 标准库的 函数(需包含 <stdlib.h> C++ 的 关键字 / 操作符(语言内置特性)
内存初始化 申请的内存 不初始化(原始内存,值随机) 支持初始化(如 new int(10) 直接赋初值 10
空间申请方式 需手动计算字节数(如 malloc(4*sizeof(int)) 直接指定类型 / 对象数量(如 new int[10] 创建 10 个 int
返回值与类型 返回 void*,使用时 必须强转(如 int* p = (int*)malloc(4) 返回对应类型指针(如 int* p = new int),无需强转
失败处理 失败返回 NULL,需 手动判空(如 if (p == NULL) 失败抛出 std::bad_alloc 异常,需捕获异常处理
自定义类型支持 仅分配 / 释放内存,不调用构造函数 / 析构函数 分配后 调用构造函数(初始化对象),释放前 调用析构函数(清理资源)

博主博主,讲了这么多是不是完了。我好不容易攒的脑细胞又用完了

:是也不是,好消息是你已经学会了如何用内存管理了,但坏消息是还没有了解内存管理方式,处理不当造成内存泄漏。

内存泄漏

什么是内存泄漏,内存泄漏的危害
什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不 是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。
比如说:
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 、事后查错型。如泄漏检测工具。
vs 下,可以使用 windows 操作系统提供的 _CrtDumpMemoryLeaks() 函数进行简单检测,该函数只报出了大概泄漏了多少个字节,没有其他更准确的位置信息。
至于其他的可以自己去找找。

❤️总结

相信坚持下来的你一定有了满满的收获。那么也请老铁们多多支持一下,为爱博,点点举报,偶布,是点点关注,收藏,点赞。❤️

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐