new/delete
1. new/delete
1.1.是什么
new/delete C++
malloc/free C
都在堆上
new相比malloc不仅分配内存,还会额外做一些初始化工作
delete相比free不仅释放内存,还会额外做一些清理工作
最明显的区别时什么呢?
class A
{
public:
A()
{
cout << "A()构造函数被调用" << endl;
}
~A()
{
cout << "~A()析构函数被调用" << endl;
}
};
int main()
{
A* p = new A();
delete p; //A()构造函数被调用
return 0; //~A()析构函数被调用
}
最明显的区别之一,就是使用new生成一个类对象时系统会调用该类的构造函数,使用delete删除一个类对象时系统会调用构造函数,既然有调用构造和析构函数的能力,意味着new和delete具备针对堆所分配的内存进行初始化(把初始化代码放在类的构造函数里)和释放(把释放相关的代码放在类的析构函数中)的能力,而这些能力是malloc和free所不具备的
1.2.operator new(—)
现在把鼠标放在new上面,会看到这个 operator new(—),放在delete上会看到operator delete(—)
这两个函数和new/delete有什么关系呢
我们知道new运算符做了两件事情
1).分配内存
2).调用构造函数初始化内存
那他是怎么分配内存的呢,其实就是new运算符通过调用operator new(—)来分配内存的,这个函数也可以直接调用的
void * mypoint = operator new(100); //分配100字节内存,一般没人这样做
delete运算符也做了两件事情
1).调用析构函数
2).释放内存
所以释放内存也是靠operator delete(—)的
1.3.new是如何记录分配的内存大小供delete使用
不同编译器的new内部有不同的实现方式
int * p = new int; // 分配出去4字节
delete p;//回收内存的时候,编译器怎么知道要回收4字节,这就是new内部的记录机制,它分配出去多少,他会找地方记录下来,回收的时候就按照这个回收
1.4.申请和释放一个数组
像上面的A类,是0字节的,在C++中,类的大小由其成员变量的大小决定。在这个代码中,类A只有默认构造函数和析构函数,因此它不包含任何成员变量。因此,类A的大小为0字节
class A
{
public:
A()
{
cout << "A()构造函数被调用" << endl;
}
~A()
{
cout << "~A()析构函数被调用" << endl;
}
};
int * p = new int[2] //如果不delete的话,泄露8字节
int a = sizeof(A); //即使这个类为空,也有1字节,因为一个类对象肯定有地址,一个内存地址至少能保存1字节
A * pA = new A(); //不delete的话,泄露1字节,这个1字节其实是A对象的大小,由于A类包含构造和析构函数,因此它才占用了内存空间,所以A对象的大小不是0字节,就和买房子一样,不可能买0平米的
A * pA = new A[2](); //不delete的话,看起来应该泄露2字节,但实际是泄露6字节
int * p = new int[2];
delete p;
delete[] p;
这两个释放内存没有区别,delete p和delete[] p的区别在于,delete p会调用一次析构函数,而delete[] p会调用每个成员的析构函数。如果数组类型是自定义类,那么new []只能用delete []来对应,new和delete对应。但是对于普通数据类型而言,他们作用的效果是一样的,例如int* p=new int,delete p和delete [] p作用效果是一样的,原因是内部普通数据类型没有析构函数。123
A * pA = new A[2]();
delete pA; // 报错
delete[] pA; //这是规范的,调用了两次析构函数,因为分配内存时候调用了两次构造
那delete[] pA为什么会调用两次A的析构呢,系统怎么知道new的时候new出来了几个数组元素(类A对象)呢
C++的做法是,在分配数组空间的时候多分配了4字节的大小(也就是上面A * pA = new A2;泄露6字节的原因,也就是为什么多出来了4字节),专门保存数组的大小,在delete[]时就可以取出这个数组大小的数字,因为数字是int,所以是4字节,就知道了需要调用析构函数多少次了
1.5.为什么new/delete new[]/delete[]要配对使用
继续上面的,就是delete p和deletep[] p的问题
记住这个结论,如果一个对象使用new[]分配内存,而用delete释放内存(不是delete[]),那么这个对象满足的条件是:对象的类型是内置类型(比如int类型)或者是无自定义析构函数的类类型
现在把类A的析构函数注释掉,就会发现
A * pA = new A[2]; //这里不再分配6字节,而是2字节
delete pA //不会报异常了
反过来看,如果A有自己的析构函数,用new[]分配,而用delete来释放,就会报错了
那这是为什么呢
因为delete pA做了两个事情
1.调用一次类A的析构函数,new时候创建了两个对象,构造函数了两次,而只释放了一个析构,则就可能出问题了,比如在构造函数中如果分配了内存,希望在析构释放,但是少了一个析构,就内存泄露了
2.调用operator delete(pA);来释放内存,系统报错其实就是执行这个调用导致的,因为多分配的这4字节的问题导致释放的内存空间错乱,比如明明释放一个0x00000012作为开始地址的内存,因为内存空间的错乱,导致释放了0x00000016作为开始地址的内存,从而导致异常
同样如果new而用delete[]也会出错,因为不断调用析构
所以要配对使用
- 点赞
- 收藏
- 关注作者
评论(0)