C++动态内存管理

目录

    • 传统艺能
  • new/delete 运算符
    • 差别
    • new 检测
    • operator new/delete
    • placement - new
    • C++内存泄漏

传统艺能

小编是双非本科大一菜鸟不赘述,欢迎大佬指点江山(QQ:1319365055)
此前博客点我!点我!请搜索博主 【知晓天空之蓝】

非科班转码社区诚邀您入驻
小伙伴们,打码路上一路向北,背后烟火,彼岸之前皆是疾苦
一个人的单打独斗不如一群人的砥砺前行
这是我和梦想合伙人组建的社区,诚邀各位有志之士的加入!!
社区用户好文均加精(“标兵”文章字数2000+加精,“达人”文章字数1500+加精)
直达: 社区链接点我

倾力打造转码社区微信公众号
在这里插入图片描述


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

new/delete 运算符

程序员都喜欢 面向对象编程,但如果你没有对象怎么办?当然是 new 一个对象出来!

new其实就是告诉计算机开辟一段新的空间,但是和一般的声明不同的是,new开辟的空间在堆上,而一般声明的变量存放在栈上。new出来的是一段空间的首地址,所以一般需要用指针来存放这段地址:

A a; // a存在栈上
A* a = new a(); // a存在堆中

其实 new 的用法除了要调用对象的构造函数,其他的属性和我们的 malloc ,calloc ,realloc 是一模一样。既然 malloc 完了需要 free 掉,那 new 也需要我们手动释放毕竟是在堆区申请的,delete 也是同样的道理,先调用析构函数再释放对象空间。当然不用 new 的话系统会自动回收空间:

delete []a;
delete a;

C++中 delete a 和 delete[] a 对于内置类型对象没有任何区别

差别

虽然 new 与 malloc 差别不大但我还是推荐使用 new ,毕竟更加简洁明了,而且 malloc 有时会面临对象不好初始化 ,如果对象包括私有类型成员且无法显式调用构造函数会生成随机值,比如构造一个栈:

class Stack
{
  private:
        int* a;
        int top;
        int capacity;
};
int main()
{
Stack* p1 = (Stack*)malloc(sizeof(Stack));
assert(p1);
Stack* p2 = new Stack;
}

因为 new 完会调用构造函数初始化,不用我们劳神费力,而且后续处理时,free 会直接干掉 Stack 这块空间,但是里面的成员还有指针类型,无法得到释放就会造成内存泄漏,delete 就会聪明的调用析构函数来解决这个问题。

对于内置类型,new 和 malloc,delete 和 free 基本一样,不同的是 new、delete 对应单个元素空间,new[] 和 delete [] 对应连续的空间。

delete 和 delete[] 两种方式体现出具体差异 ,前者仅仅释放掉 a 指向的全部内存,但是只调用了 a[0] 的析构函数,对于 a[1] 及后续成员我们习性分配的空间将不作处理从而导致内存泄漏。对于后者,会释放 a 指向的内存并逐一调用每个成员的 destructor(析构函数)。对于像 int,char,int*,struct 等等简单数据类型,由于对象没有 destructor,所以用 delete 和 delete [] 是一样的。
C++动态内存管理_第2张图片

new 检测

一般情况下, malloc 和 new 都不会失败,除非你开的非常大,但如果失败了,二者失败的各不相同,malloc 会返回一个空指针,而 new 会报错,我们针对 malloc 失败会采取 assert 策略,同样的针对 new 失败会进行异常捕获

catch (const exception& p)
{
cout << p.what() << endl; //抛异常
}

如果失败控制台就会给出 “ bad allocation” 字样提示开辟异常。

operator new/delete

new 和 delete 是我们进行动态内存申请和释放的操作符,operator new/delete 是系统提供的全局函数,operator new() 是对new的重载形式,它是一个函数,并不是运算符。

对于operator new来说,分为全局重载和类重载,全局重载是 void* :: operator new(size_t size),在类中重载形式 void* A :: operator new(size_t size),他实际上是相当于对 malloc 和 malloc 抛异常进行了封装。

他的意义其实在于 new 的底层原理,new 调用其实就是 call malloc 再 call 对象的构造函数,但其实这并不符合 C++ 的需求,malloc 失败就会返回空,而 C++ 是面向对象不允许这种情况发生,于是引入了异常机制,会先去 call operator new。

C++ 比较恶心的机制,我们 new[] 的话会多次调用 call operator new,再 call malloc,再 call 构造函数,绕了好多层去找堆,路程很远消耗很大,如果再频繁调用成本就抬高了,我们有个东西叫内存池,他比堆离得更近,有些地方甚至比堆还快。打个比方,情人节你给你女朋友发红包,堆就像小红包,你每次发 1 元掉你女朋友胃口,很难不分手快乐,而内存池就像微信转账,直接一个快准狠的 520 给拿捏了。
C++动态内存管理_第3张图片

placement - new

也称定位 new 表达式,在已分配的原始内存空间中调用构造函数初始化一个对象。其格式为:new(place_address)type 或者 new(place_address) type (initializer-list) ,其中 place_address 必须是一个指针,initializer-list 是类型的初始化列表。

他的使用场景一般就是配合内存池使用,因为内存池分配出的内存没有初始化,所以如果是自义定类型空间对象,需要 new 的定义表达式进行显式构造函数进行初始化,直接使用 new 就会直接去到堆区,无法去我要找的空间。

C++内存泄漏

C++程序中一般关心两个方面的内存泄漏:堆内存泄漏和系统资源泄露

堆内存泄漏指程序执行中依据需要用 malloc,calloc,realloc,new 的方式从堆中分配一块内存,但是用完后并没有 free 或者 delete 进行归还,这部分内存没有被释放掉,你访问不到,系统也无法将其再分配。

系统资源泄露指程序使用系统分配的资源,比如套接字,文件描述符,管道等没有使用对应函数释放掉,导致系统资源浪费,严重可导致系统效能减少,执行不稳定。

我们在Linux环境中有一款强大的开源内存检测工具——valgrind,他里面的 Memcheck 是valgrind使用最广泛的工具,一个重量级的内存检测器,能够发现绝大多数内存错误使用情况。

今天就到这里吧,润了家人们。

你可能感兴趣的