создание исключительного 2D-массива: очистка перед повторным выбросом в переполнении стека

Я хочу иметь функцию, которая динамически создает и возвращает 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?
}
}

0

Решение

Для исключительного безопасного управления памятью используйте 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?

Механизм обработки исключений создает его где-то (не в стеке, который собирается разматываться) и уничтожает его после обработки. Точно не указано, где оно находится, как оно должно работать.

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]