基本概念
【c】malloc/free从堆中分配/释放内存(malloc=memory allocation,malloc和free是函数)
//标准
void *malloc(int NumBytes); //NumBytes为要分配的字节数
void *free(void *FirstByte);
//例
int *p = (int *)malloc(sizeof(4));
free(p);
【c++】new/delete从堆中分配/释放内存(new和delete是运算符或标识符)
//指针变量名 = new 类型标识符;
int *p1 = new int;
//指针变量名 = new 类型标识符(初始值);
int *p2 = new int(0);
//指针变量名 = new 类型标识符[内存单元个数];
int *p3 = new int[3];
//释放
delete p1;
delete p2;
delete[] p3;
【注意】
(1)delete后,指针指向的地址不变,指向的内容里的东西没有了!
(2)new成功一定要delete!
(3)不能重复调用delete!
(4)能用nullptr的地方不用null!
细说
1、new、delete(关键字或运算符)同sizeof一样不是函数,与malloc和free相比多调用构造和析构函数
2、new的时候干了两件事:先分配内存(用operator new() 函数),再调用构造函数来初始化内存
delete的时候干了两件事:先调用析构函数,再释放内存(用operator delete() 函数)
3、基本new如何记录分配的内存大小供delete使用(不同编译器不同):new内部会记录字节数
4、申请、释放一个数组
A *pA = new A[2](); //泄露6字节而不是2字节,多的4字节用来记录new了几个类类型
详谈
1、new时加不加括号区别
int *p1 = new int; //初值随机
int *p2 = new int(); //初值给0
2、new对象时加括号与否的区别
int *p1 = new A;
int *p2 = new A();
1)若类A为空类:没有区别
2)若类A中有成员变量:带括号的初始化会把一些和成员变量有关的内存请零(不是全部)
3)若类A中有构造函数:m_i等成员变量的问题就交给构造函数了,带不带括号的效果一样
3、new和delete具体都干了啥(再说一遍)(测试方法:调试——窗口——反汇编)
new首先调用了operator new函数,这个函数中调用了malloc函数,然后调用了A::A()构造函数;
delete首先调用了A::~A()析构函数,然后调用了operator delete函数,这个函数中调用了free函数。
注:malloc和free这一层再往下走就不是跨平台的了。
new与malloc的区别
1、new分配内存按照数据类型进行分配,malloc分配内存按照大小分配;
2、new不仅分配一段内存,而且会调用构造函数,但是malloc则不会;同理,delete销毁的时候会调用对象的析构函数,而free则不会;
3、new返回的是指定对象的指针,而malloc返回的是void*,因此malloc的返回值一般都需要进行类型转化;
4、new是一个操作符可以重载,malloc是一个库函数;
5、new分配的内存要用delete销毁,malloc要用free来销毁;
6、malloc分配的内存不够的时候,可以用realloc扩容,new没有这样的操作。
【realloc原理】
realloc是从堆上分配内存的,当扩大一块内存空间时,realloc()试图直接从堆上现存的数据后面的那些字节中获得附加的字节,如果能够满足,自然天下太平;如果数据后面的字节不够,那么就使用堆上第一个有足够大小的自由块,现存的数据然后就被拷贝至新的位置,而老块则放回到堆上,这句话传递的一个重要的信息就是数据可能被移动。
int len = 7;
int *a = (int*)malloc(sizeof(int)*len);
a[1] = 256;
a[2] = 5;
len++;
int* aold = a;
a = (int*)realloc(a,sizeof(int)*len);
前面两句定义了1个长度为7的int型数组,每个元素的字节长度是4,所以共占 28byte 内存;
最后一句:
1)假如数组 a 内存里接着的4个字节还没被其他对象或程序占用,那么就直接把后面4个字节加给数组a,数组前面7个旧的元素的值不变, 数组a的头部地址也不变;
2)假如数组 a 内存里接着的4个字节已经被占用了,那么realloc函数会在内存其他地方找1个连续的 32byte 内存空间,把数组a的7个旧元素的值搬过去,所以数组a的7个旧元素的值也不变,但是数组a的头部地址变化了。但是这时我们无需手动把旧的内存空间释放,因为realloc函数改变地址后会自动释放旧的内存,再手动释放程序就会出错了。
7、new如果分配失败了会抛出bad_alloc的异常,而malloc失败了会返回NULL。
因此对于new,正确的姿势是采用try...catch语法,而malloc则应该判断指针的返回值。为了兼容很多c程序员的习惯,C++也可以采用new(nothrow)的方法禁止抛出异常而返回NULL。
#include<new> //必须使用new头文件
Manager * pManager = new (nothrow) Manager();
if(pManager == NULL)
{
//记录日志
return false;
}
知识补充:try-catch用法
例1:数组越界
#include<iostream>
using namespace std;
enum index{underflow, overflow};
int array_index(int *A, int n, int index);
int main()
{
int *A = new int[10];
for(int i=0; i<10; i++)
A[i] = i;
try
{
cout<<array_index(A,10,5)<<endl;
cout<<array_index(A,10,-1)<<endl;
cout<<array_index(A,10,15)<<endl;
}
catch(index e)
{
if(e == underflow)
{
cout<<"index underflow!"<<endl;
exit(-1);
}
if(e == overflow)
{
cout<<"index overflow!"<<endl;
exit(-1);
}
}
return 0;
}
int array_index(int *A, int n, int index)
{
if(index < 0) throw underflow;
if(index > n-1) throw overflow;
return A[index];
}
例2:new失败bad_alloc
int * p;
try
{
p = new int[10];
}
catch(bad_alloc)
{
cerr<<"allocate failure!"<<endl;
exit(-1);
}