c++动态内存开辟

来源:互联网 发布:数据采集 久其格格 编辑:程序博客网 时间:2024/05/29 07:33

首先,来谈一谈内存管理:
来看一道很有意思的题目:

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[]出来的,往前移动必然崩溃。