首先,来谈一谈内存管理:
来看一道很有意思的题目:
int i = 1;
static int j = 1;//i和j都在数据段(静态区),但链接属性不一样,static在外部文件不可见
void test1()
{
static int k = 1;//j和k都是static,生命周期一样,但作用域不一样,static不改变变量的作用域
int n = 1;//k和n的生命周期不一样,作用域一样
}
void test()
{
//sizeof() strlen()
int a[10] = { 1, 2, 3, 4 }; //数组 12
char a2[] = "abcd"; //数组 5 4
char* a3 = "abcd"; //指针 4 4
//a2[0]在栈,a3[0]在代码段(常量区)
}
这是我的分析:
在c语言里,如何理解:
void test()
{
int* p1 = (int*)malloc(sizeof(int)*4);
}
接下来进入正题,c++用 new/delete 来实现动态内存开辟:
void test1()
{
int* p1 = new int;
int* p2 = new int(3);
int* p3 = new int[3];
delete p1;
delete p2;
delete[] p3;
}
注意,malloc/free new/delete new[]/delete[]一定要cp使用,一定要cp。
为什么要cp?malloc/free和new/delete之间有什么关系和差异呢?
首先,malloc/free是库函数,new/delete是自定义类型,而且和构造函数,析构函数有关,那么我写一个简单的SeqList类来说明问题:
class Seqlist
{
public:
Seqlist(size_t n = 1)
:_a(new int[n])
{
cout << "构造函数" << endl;
}
~Seqlist()
{
cout << "析构函数" << endl;
delete[] _a;
}
private:
int* _a;
};
可以看出,new是在创建对象的同时还会调用构造函数,delete清理对象的同时还会调用析构函数。
SeqList* p2 = (SeqList*)malloc(sizeof(SeqList) * 3);
free(p2);
然后malloc和free不会调用,只是开辟空间和清理。
所以说,不匹配使用时极容易出错。比如:
SeqList* p1 = new SeqList[3];//调用三次构造函数
free(p1);
这里new调用了三次构造函数,但是没有和delete匹配使用,导致没有释放_a指针,存在内存泄漏。
具体分析如下:
所以p1应该这样释放:
delete[] p1;//调用三次析构函数
内存泄漏存在很大危害,这个我在研究。
当我调试的时候,new语句跳转时会出现一个函数:operator new,
同样的,delete语句也会跳转到operator delete
这里先声明一点,operator new与operator delete不是重载,它们是函数。
其实operator new是malloc的一层封装.
我们知道,malloc失败了返回0,而operator new失败了会抛异常,它们处理错误方式不同。
//库函数
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{ // try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{ // report no memory
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
可以清楚地看到封装关系。
总结:
new:1.调用operator new,operator new再调malloc分配空间。
2.调用构造函数。
delete:1.调用operator delete,operator delete再调free释放空间.
2.调用析构函数。
这里有一个问题,上面的一个代码:
SeqList* p1 = new SeqList[3];//调用三次构造函数
delete[] p1;//调用三次析构函数
既然[]里没有指定大小,那么怎么知道析构三次呢?
上文谈到:
new:1.调用operator new,operator new再调malloc分配空间。
2.调用析构函数。
调试说明问题:
这是malloc,它的res指针返回给operator new的指针p。
这里可以清楚的看到指针的地址。
但是当地址传给p1时却不是4aa0.
而是向后给了4字节,通过查阅资料,原来这四个字节才是问题的关键所在。
所以说,malloc/free new/delete new[]/delete[]一定要cp使用,一定要cp。
如果:new[] ->free,free释放返回的地址,肯定崩溃。
如果:new[] -> delete:会崩溃,因为delete会认为是new出来的,不会往前移四字节,,只析构一次,一定崩溃。
如果:new -> delete[]:会崩溃,因为delete[]会认为是new[]出来的,往前移动必然崩溃。