C++学习_动态内存管理

C++内存管理

附:C语言动态内存管理

C++管理动态内存的操作符有两组:
1.new/delete
new和delete的使用实例:

class Test
{
public:
    Test()=default;
}
int main(void)
{
    Test *t1 = new Test;//从堆申请一块Test大小的内存(自定义类型)
    int *t2 = new int;//从堆上申请了一块int大小的内存(内置类型)
    int *t3 = new int(1);//从堆上申请了一块int大小的内存并且初始化为1
    //释放申请的内存
    delete t1;
    delete t2;
    delete t3;
    return 0;
}

2.new[]/delete[]
new[]和delete[]的使用实例:

//Test类同上
int main(void)
{
    //申请了连续的10块大小为sizeof(Test)的内存
    Test *t1 = new Test[10];
    //释放掉new[]申请的内存
    delete[] t1;
}

注意: C++中的动态内存管理操作符和向下兼容C语言的malloc/free函数一定要配套使用,否则就会在某种特定情况下出现内存泄漏,甚至程序会直接运行失败,不过缓慢的内存泄漏显然更加让人头疼!

那么什么时候会出现上面所说的问题呢?

class Test
{
public:
    Test()
    {
        cout<<"Test()"<<endl;
    }
    ~Test()
    {
        cout<<"~Test()"<<endl;
    }
}
int main(void)
{
    Test *t1 = new Test;
    free(t1);
    delete[]t1;

    Test *t2 = new Test[10];
    free(t2);
    delete t2;

    int *t3 = new int;
    free(t3);
    delete[]t3;
    return 0;
}

代码解析:
1.用free释放t1指向的空间,程序不会运行失败,但是由于free不会调用对象的析构函数,所以如果对象内有申请其他的空间,而析构函数内包含对其他内存空间的释放,则程序就会出现内存泄漏。

2.用delete[]释放t1指向的空间,程序会运行失败,因为delete[]和delete的底层实现有区别,所以会出错。如果没有自定义析构函数,则不会出错,原因也是和delete[]的底层实现有关。

3.用free释放t2指向的空间,程序会运行失败,因为delete[]和free的底层实现有区别,所以会出错。

4.用delete释放t2指向的空间,程序也会运行失败,原因同3。

5.由于int是内置数据类型,所以即使不匹配程序也不会运行错误,但是可能会造成内存泄漏。

如果你仅仅想要知道C++怎样进行动态内存管理的,那么下面这段内容就可以略过不看,请直接翻到结尾。如果想要搞明白上面那些事实的原理,那么就得了解了解new/delete, new[]/delete[]的底层原理:

<———————————————————>

new/delete以及new[]/delete[]的底层原理

1.首先你得知道这两个C++中定义的函数:
operator new
operator delete
是的,这是两个函数,这两个函数和C语言中的malloc以及free没有什么区别,完全可以用malloc和free来替代这两个函数的“大部分”功能,可是既然是大部分,就说明还有区别:这两个函数会调用构造函数和析构函数。
2.底层的调用顺序:
new->operator new->malloc
delete->operator delete->free

3.动态内存分配的内幕

使用new[]的意思可以理解为申请 指定块 指定类型大小的空间,eg:
Test *t4 = new Class[10];,
那么底层的delete[]在析构对象的时候,怎么知道要调用多少次析构函数呢?

原因就在于使用new[]申请的空间除了我们需要的数据空间外,在数据空间的前面还有一段空间(4字节)保存着一个计数,这个计数就是申请了多少块指定类型的空间。new返回的地址是数据空间的地址,而不是真正申请到空间的起始地址,而使用delete[]的时候,编译器就会自动向前寻址直到计数空间的开始地址,然后得到计数,根据这个计数来决定调用几次析构函数。

而new因为是申请一块空间,所以就不用多申请那块计数空间。而delete在释放空间的时候,自然也不会向前寻址去找计数空间。

现在,上面代码的问题理解起来就没有问题了。

  1. 因为内置类型使用的是默认的析构函数,而编译器在这里做了一个优化,只要检测到析构函数不用进行资源的释放,那么在使用new[]的时候就不会多申请4个字节的空间,自然delete[]也就不会向前寻址,所以即使不配套使用,也不会有问题。

  2. 如果自定义类型的数据使用的也是缺省的析构函数,那么效果也和内置类型相同。

  3. 如果自定义类型没有使用缺省的析构函数,那么由于动态内存操作符的底层特性,就会出现段错误(Segmentation fault)。

<——————————————————–>

new/delete和new[]/delete[]以及malloc/free的区别比较
注:为了方便描述,我就直接按照书写顺序按①,②,③来代替每组操作符/函数

①和③

1.前者除了申请和释放空间,还会调用构造函数和析构函数进行对象初始化和清理工作,而后者只是申请和释放空间
2.前者是C++操作符而后者是C/C++标准库函数
3.前者返回对应的数据类型指针,而后者返回的是`void*`类型指针
4.new申请空间的时候不用手动计算空间大小,而malloc需要
5.new申请空间失败会抛异常,而malloc函数申请空间失败会返回NULL

①和②

前者申请的空间只包含数据空间,释放也只释放数据空间。后者除了会申请数据空间,在自定义类型中有非缺省析构函数时,在数据空间的前面还有一段计数空间(4字节),释放的时候会同时释放掉计数空间和数据空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值