Я хочу иметь функцию, которая динамически создает и возвращает 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;
}
Так что я могу быть уверен, что при создании бросков утечки памяти нет. Но могу ли я быть уверен, что переданное исключение e «такое же», как если бы оно было сразу брошено новым и не поймано?
Например.
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;
}
Или нужно иметь catch (bad_alloc &e)
внутри create
функционировать? Или это работает только с catch (...) { /* do clean-up*/ throw; }
? Есть ли та же проблема, что и в C # с потерей трассировки стека, когда повторный бросок не с throw;
просто?
И еще один, более общий вопрос:
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 «такое же», как если бы оно было сразу брошено новым и не поймано?
Не будет; вы бросаете новый объект, созданный копированием или перемещением e
с типом exception
,
Или нужно иметь
catch (bad_alloc &e)
внутри функции создания?
Тогда вы не поймаете другие типы исключений. В этом случае это может не быть проблемой, но вы действительно хотите поймать все исключения, если вы собираетесь убирать, как это. Повторим: не используйте обработчики исключений для очистки. Это очень подвержено ошибкам.
Или это работает только с
catch (...) { /* do clean-up*/ throw; }
?
Это отбросит исходный объект, то есть то, что вы хотите. (За исключением того, что вы не должны хотеть ловить что-либо в первую очередь).
Есть ли та же проблема, что и в C # с потерей трассировки стека, когда повторный бросок не с
throw;
просто?
Стандартные исключения в любом случае не дают вам трассировки стека. Если у вас есть пользовательский тип исключения со трассировкой стека, то это зависит от того, генерирует ли он новый при копировании / перемещении или копирует / перемещает существующий.
Где находится объект или C-String?
Механизм обработки исключений создает его где-то (не в стеке, который собирается разматываться) и уничтожает его после обработки. Точно не указано, где оно находится, как оно должно работать.
Других решений пока нет …