c++动态内存管理

C++动态内存管理

  • 对比C语言和C++的动态内存管理

  • 探索静态内存管理和动态内存管理

  • 理解什么是内存泄露及内存泄露的危害

  • operator new与operator delete

  • new的定位表达式

  • 深度探索new的内部机制

  • new与delete的实现原理


  • 1.栈又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的。
  • 2.内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共 享内存,做进程间通信。(现 在只需要了解一下)
  • 3.堆用于程序运行时动态内存分配,堆是可以上增长的。
  • 4.数据段–存储全局数据和静态数据。
  • 5.代码段–可执行的代码/只读常量/常量字符串。
C语言动态内存管理

C语言使用malloc/calloc/realloc/free进行动态内存管理

void Test ()
{

int* p1 = (int*) malloc (sizeof (int)*4);   //堆上开辟,成功返回首地址,失败返回NULL
free(p1 );

// 1.calloc/realloc/malloc 的区别是什么?
int* p2 = (int*) calloc(4, sizeof (int));
//按字节用0进行初始化
//参数: 元素个数,元素大小
int* p3 = (int*) realloc(p2 , sizeof( int)*6);
//可以更改之前申请空间的大小
//p2-->NULL 相当于malloc
//p2-->非空 size -->增大或缩小原空间
//返回原空间的地址

realloc扩容:
//检查后面是否有足够扩容的空间
//如果有直接再后面增加
//如果不够 重新寻找足够大的空间,将原来的内容拷贝过去并扩大,将原地址释放,返回新地址

// 这里需要free(p2)吗?
//不需要p2与p3指向同一块空间,relloc如果在原址上进行扩容时,只能删除一次,如果重新开辟时,原地值已经被释放了。只能被释放一次
free(p1);
free(p3 );
}


//malloc一块空间只被一个人使用

//malloc的地址分配与管理:



总结malloc/calloc/realloc的区别
malloc:正常开辟size个大小的内存

calloc:

  • 1.对开辟好的内存用0进行初始化,根据所存类型进行初始化,如果时整形,初始化为0,指针初始化为NULL,浮点型初始化为浮点0
  • 2.有两个参数 (元素个数,每个元素大小)

realloc:可进行扩容或缩小。
扩容时,如果当前指针后的空间足够,则在原址增加空间,如果当前空间后的地址不够,则重新选择一个适合的空间开辟,拷贝原地址内容,释放原地址空间,返回当前新地址。

C++动态内存管理

C++通过newdelete动态管理内存。

  • new/delete动态管理对象(申请单个空间)。

  • new[]/delete[]动态管理对象数组(申请连续空间)。

void Test ()
{

int* p4 = new int;	// 动态分配4个字节(1个 int)的空间单个数据
int* p5 = new int(3);	// 动态分配4个字节(1个 int)的空间并初始化为3
int* p6 = new int[3];	// 动态分配12个字节(3个 int)的空间

 

delete p4 ; 
delete p5 ;
delete[] p6 ;

}
  • 注意 :malloc/free、new/delete、new[]/delete[]

一定匹配使用,否则可能出现内存泄露甚至崩溃的问题

void Test ()

{

// 以下代码没有匹配使用,会发生什么?有内存泄露吗?会崩溃吗?

int* p4 = new int;
int* p5 = new int(3);
int* p6 = new int[3];

int* p7 = (int*) malloc(sizeof (int));

 delete[] p4;
 delete p5;
 free(p5); 
 delete p6;
 delete p7;


会导致程序崩溃,内存出错
}

内存管理
int globalVar = 1;

static int staticGlobalVar = 1; void Test ()

{

// 1.全局变量、全局静态变量、局部静态变量、局部变量之间的区别是什么?

static int staticVar = 1; int localVar = 1;

// 2.下面的a1和 a2和a3 有什么区别和联系?

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

char a2 [] = "abcd"; char* a3 = "abcd";

 

int * p1 = ( int*) malloc (sizeof ( int)*4);
int * p2 = ( int*) calloc (4, sizeof ( int));

int * p3 = ( int*) realloc (p2 , sizeof( int )*4);
free (p1 );

free (p3 );

 

int * p4 = new int; 
int * p5 = new int(3); 
int * p6 = new int[3];
delete p4 ;

delete p5 ;
delete [] p6 ;

}

new和delete 自定义类型

void Test2()
{
// 申请单个Test类型的对象Test* p1 = new Test; //开空间,调构造完成初始化

delete p1;
void Test2()
{
// 申请单个Test类型的对象Test* p1 = new Test; //开空间,调构造完成初始化

delete p1;
//调用析构清理,再释放空间

//申请10个Test类型的对象
Test* p2 = new Test[10]; 
//申请几个空间就调用几次构造,几次析构函数
}

在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数malloc与free不会。


【malloc/free和new/delete的区别和联系?】
  • 它们都是动态管理内存的入口。
  • malloc/free是C/C++标准库的函数,new/delete是C++操作符。
  • malloc/free只是动态分配内存空间/释放空间。而new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理(清理成员)。
  • malloc/free需要手动计算类型大小且返回值为void*,new/delete可自己计算类型的大小,返回对应类型的指针。

operator new与operator delete函数

new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数

new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间

operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
operator delete 最终是通过free来释放空间的

operator new和operator delete用户也可以自己实现,用户实现时即可实现成全局函数,也可实现成类的成员函数,但是一般情况下不需要实现,除非有特殊需求。比如需要跟踪内存的申请与释放时:

因为operotor new和operator delete 被自定义实现,所以new开辟的空间不一定在堆上。


C++的其他内存管理接口(placement版本)

void * operator new (size_t size);

void operator delete (size_t size);

void * operator new [](size_t size); 
void operator delete[] (size_t size);

总结:

  • ** operator new/operator delete operator new[]/operator delete[] 和malloc/free用法一样**。

  • 他们只负责分配空间/释放空间,不会调用对象构造函数/析构函数来初始化/清理对象

  • 实际operator new和operator delete只是malloc和free的一层封装。

//operator new=封装(malloc+失败抛异常)

//operator delete==free
//没有失败

内置类型

内置类型
new int -->operator new --> malloc+抛异常 -->构造

malloc int -->malloc

// int 有构造函数
int i=int(10);
int i(10);

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

自定义类型
new做了两件事

free 当指针出错时会失败

    1. 调用operator new分配空间。
    1. 调用构造函数初始化对象。
delete也做了两件事
  • 调用析构函数清理对象

  • 调用operator delete释放空间

new[N]
  • 调用operator new分配空间。

  • 调用N次构造函数分别初始化每个对象。

delete[]
  • 调用N次析构函数清理对象。

  • 调用operator delete释放空间。

//为了符合c++规范,如果出错要求抛异常

定位new表达式(replacement版本)

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

new (place_address) type

new (place_address) type(initializer-list)

place_address必须是一个指针,initializer-list是类型的初始化列表。
使用场景:

定位new表达式一般是配合内存池使用,因为内存池分配的内存没有初始化,如果是自定义类型的对象的话,必须使用new的定位表达式调用构造函数进行初始化。

 #include <iostream>                                                                                
    2 #include <stdlib.h>                                                                                
    3 using namespace std;                                                                               
    4 class Test{                                                                                        
    5   public:                                                                                          
    6     Test()                                                                                         
    7     {                                                                                              
    8       cout<<"Test():"<<endl;                                                                       
    9     }                                                                                              
   10     ~Test()                                                                                        
   11     {                                                                                              
   12       cout<<"~Test():"<<endl;                                                                      
   13     }                                                                                              
   14   private:                                                                                         
   15     int a;                                                                                         
   16 };
int main()                                                                                         
    {                                                                                                  
                                           
      void* p=malloc(sizeof(Test));                                                                   
      new(p) Test;
      //new(p) Test(1,3,4)
      //new的定位表达式,将原始地址空间初始化,会调用构造函数                           
    free(p);//释放时要使用free,使用delete不会调用析构函数                                          
 }

设计一个类只能在堆上开辟空间

(仿拷贝的实现)

class HeapType{
    public:
    static HeapType* GetHeapObj()  //构造函数必须使用对象来调,将函数声明成静态可以在类的全局域中访问
        {
            return new HeapType;
        }
        
        // (已删除函数)HeapType(const HeapType& p)=delete;  可以防止别人调用
        
    private:
       HeapType()
       {};
      // HeapType(const HeapType& p)//只声明不实现,因为实现可能很麻烦,本身也可能不需要
       {};      //声明私有化
    //如果在内外定义,再通过友元访问私有,所以使用已删除函数
    
    HeapType(const HeapType& p)=delete;  //断绝所有访问路径
}
HeapType p3;//在数据段
int main()
{
    //HeapType* p1=new HeapType;
    HeapType p1=HeapType::GetHeapObj();
    //在堆上动态开辟
    //HeapType p2;
    //在栈上
    //对象调用构造函数初始化
    
    //为防止拷贝构造时创建对象,所以将拷贝构造私有化
    
}

将构造函数私有化
封掉常见入口
自定义接口
拷贝构造可能会通过已存在的对象拷贝出栈上的对象

只在栈上给对象开辟空间

匿名对象

Date() 没有名字 ,声明周期只存在该行

第一种

class StackType{
    public:
    static  StackType GetStackObj()
    {
        StackType Obj;
        return obj;  //返回时会拷贝一个临时对象,obj会销毁
    }
    private:
        StackType()
        {};
    
}
int main()
{

       StackType::GetStackObi().Print(); //为一个匿名对象
       StackType obj= StackType::GetStackObi();  //通过赋值
       retutrn 0;
}

第二种

class StackType{
    public:
    static  StackType GetStackObj()
    {
        StackType Obj;
        return obj;  //返回时会拷贝一个临时对象,obj会销毁
    }
    private:
        void* operator new(size_t n)=delete;  //c++11
        void*operatjor delete(void* p)=delete;
    
int main()
{

       StackType obj1;
       
}
//未禁止代码段和数据段
如 static StackType p;
  全局的 StackType p;

内存泄漏

指向内存的指针丢失,不再使用的内存不进行释放。

内存泄漏的分类
  • 堆内存泄漏

通过malloc/realloc/calloc/new/申请的内存必须通过相应的free/delete进行释放,如果不再使用后不进行释放将会造成内存泄漏问题

  • 系统资源内存泄漏

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

  • 如何避免内存泄漏
  1. 事前预防型,如智能指针
  2. 事后查错型如内存泄漏检测工具。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值