讨论:new抛的std::bad_alloc异常

本文讨论了在使用Qt进行开发时如何处理内存分配失败的情况,包括何时及如何捕获和处理std::bad_alloc异常,提供了多种场景下的建议。

initial:

So I use Qt a lot with my development and love it. The usual design pattern with Qt objects is to allocate them using new .

Pretty much all of the examples (especially code generated by the Qt designer) do absolutely no checking for the std::bad_alloc exception. Since the objects allocated (usually widgets and such) are small this is hardly ever a problem. After all, if you fail to allocate something like 20 bytes, odds are there's not much you can do to remedy the problem.

Currently, I've adopted a policy of wrapping "large" (anything above a page or two in size) allocations in a try/catch. If that fails, I display a message to the user, pretty much anything smaller, I'll just let the app crash with a std::bad_alloc exception.

So, I wonder what the schools of thought on this are on this?

Is it good policy to check each and every new operation? Or only ones I expect to have the potential to fail?

Also, it is clearly a whole different story when dealing with an embedded environment where resources can be much more constrained. I am asking in the context of a desktop application, but would be interested in answers involving other scenarios as well.

 

A1:

The problem is not "where to catch" but "what to do when an exception is catched".

If you want to check, instead of wrapping with try catch you'd better use

 

    #include <new>
    x
= new ( std :: nothrow ) X ();
   
if ( x == NULL ) {
       
// allocation failed
   
}

My usual practice is

  • in non interactive program, catch at main level an display an adequate error message there.

  • in program having a user interaction loop, I catch also at the loop so that the user can close some things and try to continue.

Exceptionally, there are other places where a catch is meaningful, but its rare.

 

A2 :

Handle the exception when you can . If an allocation fails, and your application can't continue without that bit of memory, why bother checking for the error?

Handle the error when it can be handled, when there is a meaningful way to recover. If there's nothing you can do about the error, just let it propagate.

 

A3:

I usually catch exceptions at the point where the user has initiated an action. For console application this means in main , for GUI applications I put handlers in places like button on-click handlers and such.

I believe that it makes little sense catching exceptions in the middle of an action, the user usually expects the operation to either succeeds or completely fail.

 

A4:

Handle it in main() (or the equivalent top level exception handler in Qt)

The reason is that std::bad_alloc either happens when you exhaust the memory space (2 or 3 GB on 32 bits systems, doesn't happen on 64 bits systems) or when you exhaust swap space. Modern heap allocators aren't tuned to run from swap space, so that will be a slow, noisy death - chances are your users will kill your app well beforehand as it's UI is no longer responding. And on Linux, the OS memory handling is so poor by default that your app is likely to be killed automatically.

So, there is little you can do. Confess you have a bug, and see if you can save any work the user may have done. To be able to do so, it's best to abort as much as possible. Yes, this may in fact lose some of the last user input. But it's that very action that likely triggered the OOM situation.. The goal is to save whatever data you can trust.

 

Practise:

just let it crash!

 

#include <iostream>

#include <cstdlib>

#include <new>

using namespace std;

void memoryEater()

{

int i = 0;

double* ptr;

while (1) {

    ptr = new double[50000000];

    cerr << ++i << '/t' ;

}

}

 

void out_of_store()

{

    cerr << "/noperator new failed: out of store/n";

    exit(1);

}

 

int main()

{

    set_new_handler(out_of_store);

    memoryEater();

    cout << "Done!" << endl;

    return 0;

}

Output:

src/newfailure> g++ setnewhandler.cpp src/newfailure> ./a.out 1 2 3 4 5 6 7 operator new failed: out of store

 

 

### std::bad_alloc 异常的原因与解决方案 #### 1. std::bad_alloc 异常的成因 在 C++ 中,`std::bad_alloc` 是一个标准异常类,当动态内存分配失败时会被出。具体来说,以下情况可能导致 `std::bad_alloc` 异常: - 系统可用内存不足,无法满足请求的内存大小。 - 内存碎片化严重,尽管系统有足够的总内存,但无法找到一块连续的内存区域来满足分配请求[^1]。 例如,尝试分配一个非常大的数组可能会触发该异常: ```cpp try { int* p = new int[1000000000000]; // 请求分配过大的内存 } catch (const std::bad_alloc& e) { std::cerr << "Memory allocation failed: " << e.what() << std::endl; } ``` #### 2. 解决方案 以下是几种常见的解决方法以避免或处理 `std::bad_alloc` 异常: ##### (1)使用 `std::nothrow` 版本的 `new` 操作符 `std::nothrow` 版本的 `new` 不会异常,而是返回一个空指针。这允许程序在分配失败时进行错误处理。 示例代码如下: ```cpp int* p = new (std::nothrow) int[1000000000000]; if (p == nullptr) { std::cerr << "Memory allocation failed" << std::endl; } else { // 使用分配的内存 delete[] p; } ``` ##### (2)优化内存使用 通过减少不必要的内存分配、重用已分配的内存或调整数据结构设计,可以降低内存分配失败的风险。例如,预分配容器容量以减少频繁的内存分配操作[^3]。 示例代码如下: ```cpp std::vector<int> vec; vec.reserve(1000000); // 预分配内存 for (size_t i = 0; i < 1000000; ++i) { vec.push_back(i); } ``` ##### (3)捕获异常并优雅地处理 在可能发生内存分配失败的地方,使用 `try-catch` 块捕获 `std::bad_alloc` 异常,并采取适当的措施,如释放资源或通知用户[^1]。 示例代码如下: ```cpp try { int* p = new int[1000000000000]; // 使用分配的内存 delete[] p; } catch (const std::bad_alloc& e) { std::cerr << "Memory allocation failed: " << e.what() << std::endl; // 执行其他错误处理逻辑 } ``` #### 3. 实例说明 以下是一个综合实例,展示了如何结合多种方法处理内存分配失败的情况: ```cpp #include <iostream> #include <vector> #include <new> void allocate_memory_with_nothrow() { int* p = new (std::nothrow) int[1000000000000]; if (p == nullptr) { std::cerr << "Memory allocation failed with nothrow" << std::endl; } else { delete[] p; } } void allocate_memory_with_exception_handling() { try { int* p = new int[1000000000000]; delete[] p; } catch (const std::bad_alloc& e) { std::cerr << "Memory allocation failed: " << e.what() << std::endl; } } void optimize_memory_usage() { std::vector<int> vec; vec.reserve(1000000); // 预分配内存 for (size_t i = 0; i < 1000000; ++i) { vec.push_back(i); } } int main() { allocate_memory_with_nothrow(); allocate_memory_with_exception_handling(); optimize_memory_usage(); return 0; } ``` #### 4. 流程图说明 以下是一个简单的流程图,描述了如何处理内存分配失败的情况: ``` +-------------------+ | Request Memory | +-------------------+ | v +-------------------+ | Check Available | | Memory | +-------------------+ | v +-------------------+ +-------------------+ | Enough Memory? |--Yes->| Allocate Memory | +-------------------+ +-------------------+ | No | v v +-------------------+ +-------------------+ | Use nothrow new | | Catch bad_alloc | +-------------------+ +-------------------+ | | v v +-------------------+ +-------------------+ | Handle Nullptr | | Handle Exception | +-------------------+ +-------------------+ ``` ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值