内存管理 + 模版初阶
内存管理 + 模版初阶
- 一、内存分布
- 二、new 和 delete
- 二、new 和 delete的实现原理
- 四、定位 new
- 五、模板
- 5.1 函数模板
- 5.1.1函数模板的格式
- 5.1.2函数模板实例化
- 5.1.3函数模板匹配规则
- 5.2 类模板
一、内存分布
- 栈区:用来存储局部数据。栈区是向下生长的。
- 内存映射段:是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
创建共享内存,做进程间通信。 - 堆区:用于动态内存分配。堆区是向上生长的。
- 代码段:存储可执行的代码和只读常量。
- 数据段:存储全局数据和静态数据。
二、new 和 delete
- new和delete都是关键字,并不是函数。它们的作用是向堆区申请和释放资源。
- new 和 delete 对于内置类型:与malloc / calloc 、free类似,但是new 和 delete更加简洁,同时能够初始化。(malloc/calloc申请空间失败会返回空指针,而new申请空间失败会抛异常)。
#include<iostream>usingnamespacestd;voidtest1(){//场景1://申请一个整型类型的空间//以前的写法int*ptr1=(int*)malloc(sizeof(int));if(ptr1==nullptr){perror("malloc fail");exit(1);}//如果要求同时初始化,就应该使用calloc函数,但是只能全部初始化为0//释放空间free(ptr1);ptr1=nullptr;//新的写法// new 数据类型int*ptr2=newint;//如果要求同时初始化// new 数据类型(初始化值)int*ptr3=newint(1);//释放空间deleteptr2;deleteptr3;//////////////////////////////////////////////////////////////////////////////////////////////场景2://申请一个连续空间的整型数组//以前写法int*p1=(int*)malloc(sizeof(int)*4);if(p1==nullptr){perror("malloc fail");exit(1);}//释放空间free(p1);p1=nullptr;//新的写法// new 数据类型[数据个数]int*p2=newint[4];//如果要求同时初始化// new 数据类型[数据个数] {初始化值}int*p3=newint[4]{1,2,3,4};for(inti=0;i<4;i++){cout<<*(p3+i);}cout<<endl;//释放空间delete[]p2;delete[]p3;}intmain(){test1();return0;}- 这里要明白new 和 delete的使用方法。同时申请的是连续空间,应该使用delete[ ] 进行释放空间,保证配套使用。
- C++之所以创建new和delete,是为了解决自定义类型。
#include<iostream>usingnamespacestd;classA{public:A(int_a=0){cout<<"A()"<<endl;}~A(){cout<<"~A()"<<endl;}private:int_a;};voidtest2(){//malloc实现A*ptr1=(A*)malloc(sizeof(A));if(ptr1==nullptr){perror("malloc fail");exit(1);}//new实现A*ptr2=newA;//释放空间free(ptr1);ptr1=nullptr;deleteptr2;}可以发现new和delete会自动调用构造函数和析构函数,而malloc不会。
voidtest(){//支持初始化A*ptr3=newA(1);A*ptr6=newA(2);//自定义类型的数组A*ptr4=newA[2];//自定义类型数组的初始化A*ptr5=newA[2]{1,2};delete[]ptr5;//拷贝构造来初始化A*ptr7=newA[2]{*ptr3,*ptr6};//匿名对象初始化A*ptr8=newA[2]{A(1),A(2)};delete[]ptr8;delete[]ptr7;delete[]ptr4;deleteptr3;deleteptr6;}二、new 和 delete的实现原理
- new 的实现原理
1.调用operator new()函数来实现申请空间资源。
operator new()也是通过malloc来实现的,但是检查申请空间是否成功,并不是看返回的指针是否为nullptr,而是通过抛异常来实现的。
2.调用析构函数来进行初始化。
- new[T] 的实现原理
1.调用operator new[ ],operator new[ ] 中调用operator new()完成对T个对象空间的申请,同时还会在申请空间的初始位置额外开一块空间(用来存放T,以便析构时知道析构多少空间),返回偏移后的指针。
2.调用T次构造函数,完成对对象空间的初始化。
- delete的实现原理
1.调用析构函数,对自定义类型中的类成员进行空间释放。
2.调用operator delete()函数 ,实现对自定义类型的空间释放。operator delete()也是通过free来释放内存空间的。
- delete[ ]实现原理
1.指针偏移读取到要析构T次,然后调用T次析构函数,完成对对象空间内申请资源的成员的空间的释放。
2.调用operator delete[ ] ,operator delete[ ] 调用operator delete()完成对T个对象空间的释放。
四、定位 new
定位new是对已经分配好空间的内存调用析构函数进行初始化。
格式: new(place_adress)type 或者是 new(place_adress)type(initializer-list)- place_adress -> 分配空间的地址
- type -> 数据类型
- initialzer-list 初始化列表
常应用于内存池方面。
voidtest3(){A*ptr1=(A*)malloc(sizeof(A));new(ptr1)A(1);ptr1->~A();free(ptr1);A*ptr2=(A*)operatornew(sizeof(A));new(ptr2)A(3);ptr2->~A();operatordelete(ptr2);}重点:malloc / free 和 new / delete 的区别
- malloc申请空间失败,返回NULL;new不需要,new需要捕获异常。
- 申请自定义类型空间时,malloc/free只会开辟和释放空间,不会调用构造函数和析构函数。new会在开辟好空间后,调用构造函数进行初始化,delete会在释放空间之前会调用析构函数,对对象内申请资源进行释放。
- malloc/free 是函数,而new / delete是操作符。
- malloc对申请的空间不会初始化,new会对申请的空间进行初始化。
malloc返回void类型,new后面跟着数据类型,会返回该类型的指针。- malloc需要手动计算申请空间大小,new不需要,new后面有数据类型,多个对象,只需在[ ]内明确对象个数。
五、模板
模板分为函数模板和类模板。
模板不建议声明和定义分离,会出现链接错误。
5.1 函数模板
5.1.1函数模板的格式
template<typename T1,typename T2,……,typename Tn> 返回类型 函数名(参数列表) {}template<classT>voidswap(constT&x,constT&y){T tmp=x;x=y;y=tmp;}- typename 是关键字,意思是类型名,也可以用class来代替。
- T是指数据类型
5.1.2函数模板实例化
函数模板示例化分为隐式实例化和显式实例化。
隐式实例化:通过实参,编译器来推断数据类型。
template<classT>TAdd(constT&x,constT&y){return(x+y);}intmain(){cout<<Add(2,3)<<endl;;//自动推导T类型为intcout<<Add(1.2,3.4)<<endl;//自动推导T类型为double/* Add(1.2,3); 这样的语句会报错,编译器推导不出T的类型到底是int还是double */return0;}显式实例化:手动来确定类型。
template<classT1,classT2>T2Add(constT1&x,constT2&y){return(x+y);}intmain(){cout<<Add<int,double>(1,2.2)<<endl;return0;}如果实参类型与手动设置类型不一致,会进行类型转化,如果不能类型转换,就会报错。
5.1.3函数模板匹配规则
1. 非模板函数与函数函数可以以相同名字存在,同时也允许模板函数的实例化与非模板函数一致。
2. 非模板函数与函数模板实例化后一致,那么在调用函数时,优先使用非模板函数。如果函数模板能够产生更加匹配的函数,那么就会使用函数模板。
template<classT1,classT2>T2Add(constT1&x,constT2&y){return(x+y);}doubleAdd(constintx,constdoubley){returnx+y;}intmain(){cout<<Add(1,2.2)<<endl;return0;}3.函数模板不会类型转化,而普通函数会类型转化。
5.2 类模板
格式: template<typename T1,typename T2,……,typename Tn> class 类名 { //类内成员定义 }类模板实例化:需要用<>指定类型。
template<classT>classStack{public:Stack(intn=4){_arr=newT[n];_size=0;_capacity=n;}~Stack(){delete[]_arr;_arr=nullptr;_size=_capacity=0;}private:T*_arr;int_size;int_capacity;};intmain(){Stack<int>s1;Stack<char>s2;}