Как обрабатывать уничтожение объекта в случае ошибки или не в случае ошибки - PullRequest
0 голосов
/ 25 августа 2011

У меня есть программа, которая отвечает за чтение данных, их форматирование и создание записей и вывод записей в файлы.Важные классы для этого обсуждения:

  • RecordGenerator - содержит поток, который контролирует основной поток (получение данных, формат, вывод)
  • FileManager - управляет выходными файлами.Записи отправляются в этот класс, который затем помещает его в файл начисления платы.
  • OutputFile - абстракция файла, который содержит записи, имеет print (), close () и т. Д. Эти объектыпринадлежат FileManager

Во время нормального завершения процесса все деструкторы для этих классов вызываются, что приводит к сбросу всех оставшихся записей в текущий выходной файл и затем к его закрытию.Это гарантирует, что мы не потеряем данные.Однако в случае ошибки нам нужно завершить работу, но мы не хотим сбрасывать и закрывать файл, поскольку данные, скорее всего, повреждены.Обычно происходит исключение, которое попадает в RecordGenerator, которое затем решает, является ли это фатальной ошибкой или нет.Если это так, он инициирует закрытие приложения.Именно в этот момент FileManager разрушается, но ему необходимо знать, есть ли ошибка.Точно так же, когда FileManager разрушается, это вызывает разрушение OutputFile, что также должно знать, есть ли ошибка.

Моей первой реакцией было просто добавить несколько открытых функций, которые устанавливают флаги ошибокдля этих классов RecordGenerator может вызвать FileManager::setErrorFlag(), который затем может вызвать OutputFile::setErrorFlag().Добавление такой цепочки кажется мне довольно неприятным запахом, особенно если учесть, что цепочка объектов может быть намного длиннее этой.

Есть ли какой-нибудь лучший способ справиться с подобным сценарием?

1 Ответ

1 голос
/ 25 августа 2011

Это типичная проблема, когда люди начинают использовать RAII так, как он не предназначен для использования.Деструкторы должны очистить ресурсы и вернуть все, за что они ответственны.Они должны не фиксировать изменения.Типичный исключительный код C ++ выглядит следующим образом:

  • выделить ресурс
  • сделать что-то
  • зафиксировать изменения

Например:

X& X::operator = (const X& x)
{
    X y(x); // allocate
    this->swap(y); // commit
    return *this;
}

void f()
{
    Transaction t(...); // begin transaction
    // operate
    t.commit(); // commit transaction
}

void g()
{
    File f(...); // open file
    // write to file
    f.flush(); // flush the buffers, this may throw but not f.~File()
}
...