【C++】C&C++内存管理

简介: 【C++】C&C++内存管理

本章知识大致总结


1.png


前言



我们的计算机,为了更好的对内存空间进行管理,将内存空间划分为以下几个区域:栈区、内存映射段、堆区、数据段、代码段,以及内核空间。C与C++在内存空间的分布是一致的。


内存分布



栈区


栈又叫堆栈,用来存放我们的临时变量(非静态局部变量、函数参数、返回值等),临时变量的特点就是出了作用域就会自动销毁。栈是向下增长的。即从高地址--->低地址使用。


内存映射段


内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口,创建共享共享内存,做进程间通信。(这里我们只需要简单了解,还未接触到)



堆区用于程序运行时动态内存分配。一般由程序员自己动态开辟,并且手动释放。(malloc/realloc/calloc/还有即将学习的new等)堆区是向上增长,即低地址--->高地址。


数据段


也就是我们说的静态区,用来存放全局数据、static修饰的静态数据。


代码段


用来存储可执行代码、只读常量


举个具体的例子,如下:


2.png


C语言动态内存管理



在 C语言阶段,我们是使用 malloc/calloc/realloc用来进行动态内存管理的,搭配 free进行释放,关于这三者之间的使用方法以及注意事项,在之前的一篇文章中,有更加详细的介绍,这里就简单提一嘴。更为详细的介绍还请参考之前的文章, 点击跳转。


3.png


C++动态内存管理



C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因


此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。


new使用方式:


// 动态申请一个int类型的空间,不做初始化
  int* ptr1 = new int;
  // 动态申请一个int类型的空间并初始化为10
  int* ptr2 = new int(10);
  // 动态申请10个int类型的空间,并将前四个元素初始化为1 2 3 4 ,其余初始化为0
  int* ptr3 = new int[10]{1,2,3,4};

我们发现,new的使用极其简单,没有类型强转,也没有空指针的检查,并且new还有一个与C语言malloc最大的区别,就是new一个自定义类型对象时,会自动调用该自定义类型的构造函数。


4.png


总结:


用法极其简单,new可以初始化,也可以不初始化。单个空间用()初始化,数组空间用{}初始化。


new一个数组空间时,用[],方括号内为元素个数。


对于自定义类型,除了开辟空间,还会调用该自定义类型的构造函数


delete的使用方式


delete后面直接跟要释放的空间地址,这里需要注意的是,在delete一个数组空间时,要用delete[],与new 中的[]配套使用。如下:


// 动态申请一个int类型的空间,不做初始化
  int* ptr1 = new int;
   delete ptr1;
  // 动态申请一个int类型的空间并初始化为10
  int* ptr2 = new int(10);
  delete ptr2;
  // 动态申请10个int类型的空间,并将前四个元素初始化为1 2 3 4 ,其余初始化为0
  int* ptr3 = new int[10]{1,2,3,4};
  delete[] ptr3;
  //注意:假如new中有[],delete也要加[]


当然,与free的最大不同之处就在于delete一个自定义类型时,会先调用该自定义类型的析构函数。


5.png


总结


delete与new搭配使用,new一个数组时用[],[]内为数组元素个数,同样,在delete时,也要用delete[]


对于自定义类型空间的释放,会先调用该自定义类型的析构函数


operator new与operator delete函数



我们看到operator,可能第一反应就是运算符重载,实际上这两个是个例外。operator new 和operator delete是系统提供的全局函数。我们可以观察它们的反汇编,来查看它们的底层实现。


6.png


因此,我们可以这么来说,operator new与operator delete可以看作是malloc与free的封装。


具体使用方法与malloc/free一致的。


//malloc
int* arr1=(int*)malloc(sizeof(int));
//operator new
int* arr2=(int*)operator new(sizeof(int));
//free
free(arr1);
//operator delete
operator delete(arr2);


深入探究new与delete



对于内置类型(int char double...)


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


对于自定义类型


new的原理


先调用operator new函数进行开辟空间


再调用析构函数


7.png


delete的原理


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


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


8.png


new T[N]的原理


先调用operator new[],然后在operator new[]中调用N个operator new函数进行开辟N个对象空间。


再调用N次构造函数


delete[] 的实现原理


先调用N次析构函数,实现对资源的清理


再调用operator delete[] ,然后operator delete[]中调用N次operator delete,进行空间释放。


结论:一定要注意匹配使用,不要混着用,不然释放的最终结果是不确定的,对于普通的内置类型,编译器不会报错,但是对于一些特殊情况,则会出现问题,这里简单谈一谈。


开辟空间、释放空间使用不匹配问题


第一种情况:内存泄漏(编译器不会报错)

假如类成员变量中有成员向内存申请开辟空间,此时假如用free,就会造成内存泄漏。如下:


9.png


第二种情况:空间未完全释放

我们在给自定义类型开辟一个数组空间时,new与delete都用到了[],这里谈一谈[]的作用,实际上,在使用new开辟空间的时候,会在最前端先开辟一个整型空间,用来标记[]内的数字。但是返回给我们的是[]后面的空间的起始位置。如下:


10.png


总结起来就一句话:配套使用即相安无事!


定位new表达式



定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。简单来说,就是对已经存在的空间进行初始化。(目前了解即可)


class A
{
public:
    A(int a=10)
        :_a(a)
    {}
    ~A()
    {
        _a = 0;
    }
    void Print()
    {
        cout << _a << endl;
    }
private:
    int _a;
};
int main()
{
    A* ps = (A*)malloc(sizeof(A));
    if (ps == NULL)
    {
        perror("malloc fail");
    }
    ps->Print();      // ps现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没
    //有执行 ,此时_a的值为随机值
    //此时我想对这块已有空间进行初始化,用定位new,调用A的构造函数对ps指向空间初始化
    new(ps)A;
    ps->Print();  //此时_a==10  
    //也可以进行传参,再次进行初始化
    new(ps)A(100);
    ps->Print();    //此时 _a==100
    return 0;
}


使用场景:


定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。


所谓内存池就是从堆上申请出一块内存,放在内存池里,这样后面再申请空间时就不需要从堆上申请,而是直接拿内存池里的空间,会一定程度上提高效率。但是由于内存池里的空间都是未进行初始化的,而定位new就是对已经存在的空间进行初始化,作用在此。


相关文章
|
6天前
|
存储 缓存 编译器
【硬核】C++11并发:内存模型和原子类型
本文从C++11并发编程中的关键概念——内存模型与原子类型入手,结合详尽的代码示例,抽丝剥茧地介绍了如何实现无锁化并发的性能优化。
|
2月前
|
存储 缓存 C语言
【c++】动态内存管理
本文介绍了C++中动态内存管理的新方式——`new`和`delete`操作符,详细探讨了它们的使用方法及与C语言中`malloc`/`free`的区别。文章首先回顾了C语言中的动态内存管理,接着通过代码实例展示了`new`和`delete`的基本用法,包括对内置类型和自定义类型的动态内存分配与释放。此外,文章还深入解析了`operator new`和`operator delete`的底层实现,以及定位new表达式的应用,最后总结了`malloc`/`free`与`new`/`delete`的主要差异。
56 3
|
2月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
140 4
|
3月前
|
存储 程序员 编译器
简述 C、C++程序编译的内存分配情况
在C和C++程序编译过程中,内存被划分为几个区域进行分配:代码区存储常量和执行指令;全局/静态变量区存放全局变量及静态变量;栈区管理函数参数、局部变量等;堆区则用于动态分配内存,由程序员控制释放,共同支撑着程序运行时的数据存储与处理需求。
178 21
|
3月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
3月前
|
存储 C语言 C++
【C++打怪之路Lv6】-- 内存管理
【C++打怪之路Lv6】-- 内存管理
56 0
【C++打怪之路Lv6】-- 内存管理
|
3月前
|
存储 C语言 C++
【C/C++内存管理】——我与C++的不解之缘(六)
【C/C++内存管理】——我与C++的不解之缘(六)
|
3月前
|
程序员 C语言 C++
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
94 1
|
3月前
|
编译器 C语言 C++
详解C/C++动态内存函数(malloc、free、calloc、realloc)
详解C/C++动态内存函数(malloc、free、calloc、realloc)
430 1
|
3月前
|
存储 安全 程序员
【C++篇】深入内存迷宫:C/C++ 高效内存管理全揭秘
【C++篇】深入内存迷宫:C/C++ 高效内存管理全揭秘
116 3