外加3:动态内存申请的结果

本文探讨了C++中的动态内存分配,包括malloc和new操作在内存不足时的行为。当内存申请失败,malloc返回NULL,而new可能抛出std::bad_alloc异常。还详细解释了new_handler函数的作用,用于处理内存不足的情况,并提供了自定义内存处理函数的方法。最后,通过三个编程实验展示了不同编译器在动态内存分配上的差异,强调了代码移植性的重要性。
摘要由CSDN通过智能技术生成

---- 整理自狄泰软件唐佐林老师课程

1. 问题

动态内存申请一定成功吗?

1.1 常见的动态内存分配代码

在这里插入图片描述

1.2 事实

  • malloc函数申请失败时返回NULL值
  • new关键字申请失败时(根据编译器不同)
    • 返回NULL值
    • 抛出std::bad_alloc异常

在这里插入图片描述

1.3 思考

new语句中的异常是怎么抛出来的?

1.4 new关键字在C++规范中的标准行为

  • 在堆空间申请足够大的内存
    • 成功
      在获取的空间中调用构造函数创建对象
      返回对象的地址
    • 失败
      抛出std::bad_alloc异常
  • new在分配内存时:
    • 如果空间不足,会调用全局的new_handler()函数
    • new_handler()函数中抛出std::bad_alloc异常
  • 可以自定义new_handler()函数
    • 处理默认的new内存分配失败的情况

1.5 new_handler()的定义和使用

在这里插入图片描述

  • 通过set_new_handler(my_new_handler)函数告知编译器内存不足时,调用my_new_handler()
    • 参数说明
      • my_new_handler应为无参数且返回值类型为void
      • my_new_handler可以尝试获得更多的可用空间,也或者抛出异常,也或者终止程序。
      • my_new_handler如果是一个空指针,处理函数将被重置为默认值(将会执行抛出bad_alloc异常)。
    • 返回值
      • 返回先前被设置的处理函数指针;如果尚未被设置或者已被重置,将返回空指针。
      • 返回的函数指针是无参的void返回值类型的函数指针。

1.6 问题

如何跨编译器统一new的行为,提高代码移植性?

1.6.1 解决方案

  • 全局范围(不推荐)
    重新定义new/delete的实现,不抛出异常
    自定义new_handler()函数,不抛出任何异常
  • 类层次范围
    重载new/delete,不抛出任何异常
  • 单次动态内存分配
    使用nothrow参数,指明new不抛出异常

1.6.2 编程实验:动态内存申请

  • 实验1
#include <iostream>
#include <new>
#include <cstdlib>
#include <exception>
using namespace std;
class Test {
    int m_value;
public:
    Test() {   
        cout << "Test()" << endl;     
        m_value = 0;
    }
    ~Test() {
        cout << "~Test()" << endl;
    }
    void* operator new (unsigned int size) {
        cout << "operator new: " << size << endl;        
        // return malloc(size);
        return NULL;
    }
    void operator delete (void* p) {
        cout << "operator delete: " << p << endl;
        free(p);
    }
    void* operator new[] (unsigned int size) {
        cout << "operator new[]: " << size << endl;
        return malloc(size);
    }
    void operator delete[] (void* p) {
        cout << "operator delete[]: " << p << endl;
        free(p);
    }
};
void my_new_handler() {
    cout << "void my_new_handler()" << endl;
}
void ex_func_1() {
    new_handler func = set_new_handler(my_new_handler);
    try {
        cout << "func = " << func << endl;
        if (func) {
            func();
        }
    } catch (const bad_alloc&) {
        cout << "catch (const bad_alloc&)" << endl;
    }
}
int main(int argc, char *argv[]){
    ex_func_1();
    return 0;
}

在这里插入图片描述
注:不同的编译器行为会有不同,bcc编译器下new会先调用全局的new_handler函数,使得set_new_handler的返回值func不为空,接着调用该func后抛出bad_alloc异常。如下:
在这里插入图片描述

  • 实验2
#include <iostream>
#include <new>
#include <cstdlib>
#include <exception>
using namespace std;
class Test {
    int m_value;
public:
    Test() {   
        cout << "Test()" << endl;     
        m_value = 0;
    }
    ~Test() {
        cout << "~Test()" << endl;
    }
    void* operator new (unsigned int size) throw() {
        cout << "operator new: " << size << endl;        
        // return malloc(size);
        return NULL;
    }
    void operator delete (void* p) {
        cout << "operator delete: " << p << endl;
        free(p);
    }
    void* operator new[] (unsigned int size) throw() {
        cout << "operator new[]: " << size << endl;
        // return malloc(size);
        return NULL;
    }
    void operator delete[] (void* p) {
        cout << "operator delete[]: " << p << endl;
        free(p);
    }
};

void ex_func_2() {
    Test* pt = new Test();
    cout << "pt = " << pt << endl;
    delete pt;
}

int main(int argc, char *argv[]){
    ex_func_2();
    return 0;
}

在这里插入图片描述
在这里插入图片描述

  • 实验3
#include <iostream>
#include <new>
#include <cstdlib>
#include <exception>
using namespace std;
class Test {
    int m_value;
public:
    Test() {   
        cout << "Test()" << endl;     
        m_value = 0;
    }
    ~Test() {
        cout << "~Test()" << endl;
    }
};

void ex_func_3() {
    int* p = new(nothrow) int[10];
    delete[] p; 
    int bb[2] = {0};    
    struct ST {
        int x;
        int y;
    };
    ST* pt = new(bb) ST();
    pt->x = 1;
    pt->y = 2;
    cout << bb[0] << endl;
    cout << bb[1] << endl;
    pt->~ST();
}

int main(int argc, char *argv[]){
    ex_func_3();
    return 0;
}

在这里插入图片描述

1.6.3 实验结论

  • 不是所有的编译器都遵循C++的标准规范
  • 编译器可能重定义new的实现,并在实现中抛出bad_alloc异常
  • 编译器的默认实现中,可能没有设置全局的new_handler()函数
  • 对于移植性要求较高的代码,需要考虑new的具体细节

2. 小结

  • 不同的编译器在动态内存分配上的实现细节不同
  • malloc函数在内存申请失败时返回NULL值
  • new关键字在内存申请失败时:
    • 可能返回NULL值
    • 可能抛出bad_alloc异常
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

uuxiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值