创建2D数组时出现异常:在C++中重新抛出之前进行清理
exception creating 2D Array: clean-up before re-throw in C++
我想要一个函数,它可以动态创建并返回2D数组,或者当内存分配失败时,在清理已分配的行后,通过异常而不丢失信息:
double **create (int rows, int cols)
{
double **array = new double* [rows];
for (int x=0; x<rows; x++)
{
try { array[x] = new double [cols]; }
catch (exception &e)
{
x--;
for (; x>=0; x--)
delete[] array[x]; // clean up already allocated rows
delete[] array;
throw e; // pass exception
}
for (int y=0; y<cols; y++)
array[x][y] = 0; // initialize array
}
return array;
}
所以我可以肯定,如果create抛出,就不会出现内存泄漏。但我能确定,传递的异常e是否与new抛出的directy"相同"而未被捕获吗?
例如
int main ()
{
double **d;
try { d = create (HUGE_x, HUGE_y); }
catch (exception &e)
{
// 1. e could be thrown by new double* [rows]
// e.g. if HUGE_x is already to huge
// 2. e could be thrown by throw e
// e.g. if after some rows no memory anymore
// in both cases: is e the same?
}
return 0;
}
或者create
函数中是否有必要包含catch (bad_alloc &e)
?还是仅适用于catch (...) { /* do clean-up*/ throw; }
?当重新抛出不是简单地使用throw;
时,是否存在与C#中丢失堆栈跟踪相同的问题?
还有另一个更普遍的问题:
void f () { throw Object(); } // or throw "help";
void main ()
{
try { f(); }
catch (Object &e) // or catch (char *)
{
// Where is the Object or C-String created on the stack in function f()
// since we aren't any more in function f() but we are dealing with
// references/pointers to a non-existent stack?
}
}
对于异常安全内存管理,请使用RAII。与其处理原始指针和异常处理程序,不如将资源分配给一个类,该类将在销毁时释放资源。这样,如果抛出异常,所有内容都会自动清理。
在这种情况下,std::vector
是管理动态阵列的合适的RAII类:
vector<vector<double>> create (int rows, int cols) {
return vector<vector<double>>(rows, vector<double>(cols));
}
(请注意,将2D数组表示为rows*cols
大小的单个数组可能更有效,访问者可以为其提供2D索引。但这不是这个问题的主题,所以我不会详细介绍)
为了回答您的问题,尽管如果您编写异常安全代码,它们在很大程度上是不重要的:
但我能确定,传递的异常e是否与new抛出的directy"相同"而未被捕获吗?
不会;您正在抛出一个新对象,该对象是通过复制或移动e
创建的,类型为exception
。
或者是否有必要在create函数中包含
catch (bad_alloc &e)
?
这样就不会发现其他类型的异常。在这种情况下,这可能不是问题,但如果你要这样清理,你真的想捕获所有异常。重申一下:不要使用异常处理程序来清理。它非常容易出错。
还是仅适用于
catch (...) { /* do clean-up*/ throw; }
?
这将重新考虑原始对象,这正是您想要的。(除了你一开始不想抓到任何东西)。
当重新抛出不是简单地使用
throw;
时,是否存在与C#中丢失堆栈跟踪相同的问题?
无论如何,标准异常都不会给您提供堆栈跟踪。如果您有一个带有堆栈跟踪的自定义异常类型,那么这取决于它是在复制/移动时生成新的异常类型,还是复制/移动现有的异常类型。
Object或C字符串在哪里?
异常处理机制会在某个地方创建它(而不是在即将展开的堆栈上),并在处理后销毁它。它没有具体说明它在哪里,只是它必须如何工作。
虽然这个问题现在已经很老了,并且已经得到了充分的回答,但我想添加一个关于重新思考异常的注释。在标准C++11中,您可以通过使用重新思考来保留原始异常信息
std::nested_exception
和std::throw_with_nested
仅供参考:使用此功能,您还可以生成异常回溯,如StackOverflow中所述,无需调试器或繁琐的日志记录,只需编写一个适当的异常处理程序即可重新抛出嵌套的异常。
由于您可以对任何派生的异常类执行此操作,因此可以向这样的回溯添加大量信息!你也可以看看我在GitHub上的MWE,那里的回溯看起来像这样:
Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
- OpenGL - 在抛出"__gnu_cxx::recursive_init_error"实例后终止调用?
- 多个文件的内存分配错误"在抛出 'std :: bad_alloc' what (): std :: bad_alloc 的实例后终止调用" [C++]
- 从构造函数抛出异常时如何克服内存泄漏
- GCC对可能有效的代码抛出init list生存期警告
- 如何在文件和行号中抛出错误
- 我收到以下错误:抛出'std::bad_alloc'实例后终止调用
- cmath抛出错误C2062、C2059、C2143和C2447.cmath包含在矢量文件中
- C++:Application.cpp中抛出了未解析的外部符号(解决方案在问题的末尾,供未来的读者参考)
- Vulkan验证层不断在VkQueuePresentKHR()上抛出图像布局错误
- 如何通过参数抛出错误消息
- 函数如何通知用户它基于函数原型抛出异常?
- 为什么 boost::interprocess::managed_shared_memory 在施工时会抛出 boost
- 动态构造函数中的新字符 [] 抛出"损坏的顶部大小";
- .exe应用程序在windows10中创建新模块时抛出错误,但在windows7中工作正常
- 较新版本的GCC抛出reinterpret_cast错误
- 操作员新不会在Android上抛出bad_alloc
- 尽管没有定义标题,但新的抛出bad_alloc <new> ?
- 我相信这是clang中的一个错误,它与构造函数抛出的placement新表达式有关
- 尝试在Visual Studio 2013中创建web服务器对象的新实例时抛出错误
- 新 int[] 抛出'Access Violation'异常