C++动态内存管理

来源:互联网 发布:网络接入服务商怎么查 编辑:程序博客网 时间:2024/05/21 21:01

一、知识回顾:
1、C语言中使用malloc/calloc/realloc/free进行动态内存管理。
其中malloc与calloc区别是:malloc开辟空间时没有初始化,calloc开辟空间时初始化为0。
realloc一般用于扩容,包含两个参数(指针,容量大小),当指针为空时,等同于malloc;当指针不为空时:1)当指针后的空间大于等于要开辟的空间时,接着原来的空间开辟;2)当指针后的空间小于要开辟的空间时,重新开辟一块空间(该空间=原来空间大小+需要开辟空间的大小),并将原来空间的内容拷贝进去。

void Test(){int *p1=(int *)malloc(sizeof(int));free(p1);int *p2=(int *)calloc(4,sizeof(int));int *p3=(int *)realloc(p2,sizeof(int)*6);free(p3);

上面的代码不用对p2进行free(),因为把p2给了p3,释放掉p3即可。

2、C++*通过new和delete动态管理内存。*
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[]一定要匹配使用,否则可能出现内存泄漏甚至崩溃的问题。

3、内存管理:

 int globalVar=1; static int staticGlbalVar=1; void Test() { //1)全局变量、全局静态变量、局部静态变量、局部变量之间的区别是什么?    static int ststicVar=1;    int localVar=1;//2)下面的a1和a2和a3有什么区别和联系?int a1[10]={1,2,3,4};char a2[]="abcd";char* a3[]="abcd";}    

解释:
1)局部变量存储于栈中;全局变量、全局静态变量和局部静态变量存储于静态区(数据段),其中全局静态变量和全局变量生命周期一样,区别是:链接属性不同,全局静态变量保持只在当前文件可见;局部变量和局部静态变量存储区不同,生命周期不同。
2)a1是静态变量,存储于栈中;a2是字符串常量,存储于常量区(代码段);a3是地址常量,存储于常量区。
这里写图片描述

注:
1>栈又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的。
2>内存映射段是高效I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
3>堆用于程序运行时动态内存分配,堆是向上增长的。
4>数据段——即静态区,存储全局数据和静态数据。
5>代码段——即常量区,存储可执行的代码/只读常量。

4、警告:动态内存分配相关的三种程序错误:
1)删除(delete)指向动态内存分配内存的指针失败,因而无法将该块内存返还给自由存储区。删除动态分配内存失败称为“内存泄漏”。
2)读写已删除的对象。如果删除指针所指的对象后,将指针置为0值,则比较容易检测这类错误。
3)对同一个内存空间使用两次delete表达式。当两个指针指向同一个动态创建的对象,删除时就会发生错误。

二、知识总结
1:总结并剖析malloc/free和new/delete之间关系和差异。
1)malloc/free和new/delete都是动态管理内存的入口。
2)malloc/free是C/C++标准库的函数,new/delete是C++操作符。
3)malloc/free只是动态分配内存空间/释放内存空空间;而new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理(清理成员)。
4)malloc/free需要手动计算类型大小,且返回值为void*,new/delete可以自己计算类型的大小,返回对应类型的指针。

class Array{public:    Array(size_t size=10)    :_size(size)    ,_a(0)    {      cout<<"Array(size_t size)"<<endl;      if(_size>0)      {          _a=new int[size];      }   }   ~Array()   {       cout<<"~Array()"<<endl;       if(a)       {           delete[] _a;           _a=0;           _size=0;        }   }private:    int* _a;    size_t _size;}void Test(){    Array* p1=(Array*)malloc(sizeof(Array));    Array* p2=new Array;    Array* p1=new Array(20);    Array* p1=new Array[10];    free(p1);    delete p2;    delete p3;    delete[] p4;}

注:new对于内置类型与malloc作用等同,但对于自定义类型,new开辟空间,且调用构造函数初始化,调用析构函数清理。

2:剖析new/delete、new[]/delete[]到底做了些什么事情。
1)C++的其他内存管理接口
void* operator new(size_t size)
void operator delete(size_t size)
void* operator new[](size_t size)
void operator delete(size_t size)
1>operator new/operator delete operator new[]/operator delete[]和malloc/free用法一样,区别是:开辟动态内存不成功时抛异常,而malloc返回0。
2>operator new/operator delete operator new[]/operator delete[]只负责分配空间/释放空间,不会调用构造函数/析构函数来初始化、清理对象。
3>operator new/operator delete 实际只是malloc/free的一层封装。

2)new、delete、new[]、delete[]
new做了两件事:
a)调用了operator new分配空间
b)调用了构造函数初始化对象
delete做了两件事:
a)调用析构函数清理对象
b)调用operator delete释放空间
new[]:
a)调用了operator new分配空间
b)调用了N次构造函数初始化N个对象
delete[]:
a)调用N次析构函数清理对象
b)调用operator delete释放空间

3)定位new表达式(replacement版本)
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
new(pleace_address)type
new(pleace_address)type(initializer-list)
pleace_address必须是一个指针,initializer-list是类型的初始化列表。

class Array{ public:      Array(size_t size=10)           :_size(size)           ,_a(0)      {         cout<<"Array(size_t size)"<<endl;         if(_size>0)         {             _a=new int [size];         }     }     ~Array()     {          cout<<"~Array()"<<endl;          if(_a)          {             delete[] _a;             _a=0;             _size=0;          }     }private:    int* _a;    size_t _size;}void Test(){      //1.malloc/free+定位操作符new()/显示调用析构函数,模拟new和delete   Array* p1=(Array*)malloc(sizeof(Array));   new(p1)Array(100);   p1->~Array();   free(p1);   //2.malloc/free+多次调用定位操作符new()/显示调用析构函数,模拟new[]和delete[]   Array* p2=(Array*)malloc(sizeof(Array)*10);   for(int i=0;i<10;++i)   {      new(p2+i)Array;   }   for(int i=0;i<10;++i)   {      p2[i].~Array();   }   free(p2);}

注意:分析delete[]的对象是自定义类型时,是怎么知道要调用N次析构函数的?
解析:当用自定义类型,显示定义析构函数并调用时,会在头部开4或8字节空间存储对象个数N,方便析构时知道调用析构多少次。
当使用new[]开辟10个对象的空间时,如下图:
这里写图片描述
如果接着选用deltete[]时,会同时清理释放多开的空间;而如果选用deltete,多开的空间未释放,存在内存泄漏,程序会崩。
故而:
当没有显示定义析构函数时,new、new[]与delete、delete[]随便匹配,程序不会崩;因为没有显示定义析构函数,编译器对调用默认析构函数这一步进行了优化,程序也不会崩。当显示定义析构函数时,new、new[]与delete、delete[]必须匹配。
3:实现NEW_ARRAY/DELETE_ARRAY宏,模拟new[]/delete[]申请和释放数组。

//模拟new[]#define NEW_ARRAY(ptr,type,n)do{    //1.开空间,使用operator new,同时开出4个字节存储个数    ptr=operator new(sizeof(type)*n+4);    *((int *)ptr)=n;    ptr=(type*)((char*)ptr+4);//指针调过用于存储个数的空间    //2.初始化,使用定位操作符new()    for(int size_t=0;i<n;++i)    {       new(ptr+i)type;    } }while(0); //模拟delete[] #define DELETE_ARRAY(ptr,type) do {    //获得需要调析构函数的个数n,并调用n次析构函数    size_t n=*((int *)ptr-1);    for(int size_t=0;i<n;++i)    {       ptr[i].~type();    }    //释放空间,调用operator delete    operator delete((char *)ptr-4));  }while(0);
原创粉丝点击