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