Должен ли я написать конец файла в деструкторе?

У меня есть код, который выглядит примерно так:

void writeToStream( std::ostream & outputStream )
{
MyXmlWriter xmlWriter{ outputStream };
xmlWriter.addNode();
xmlWriter.addNode();
xmlWriter.close(); // should this be called in `MyXmlWriter` destructor?
}

Функция close записывает некоторые закрывающие теги xml, чтобы файл можно было правильно проанализировать. Конструктор записывает заголовок XML-файла. Можно подумать xmlWriter.close(); код очистки. C ++ часто советует вводить код очистки в деструкторы. Таким образом, вы никогда не забудете правильно очистить. Однако в нашем случае код очистки может сгенерировать. (Представь это file могут быть включены исключения, запись в файл может произойти сбой.) Поэтому, если close() функция вызывается в деструкторе, затем она должна быть заключена в блок try-catch, который съедает все сгенерированные исключения:

MyXmlWriter::~MyXmlWriter()
{
try
{
close();
}
catch (...)
{
}
}

Однако в этом случае вызывающая сторона не будет уведомлена о каких-либо ошибках. Функция writeToStream() может не записать закрывающие теги xml в файл, если вызывающий не узнает об этом. Какова лучшая практика в этой ситуации?

2

Решение

Что ты закрываешь? В общем закрываем файл открытым для записи (std::ostream, FILE* или системно-зависимый файловый дескриптор) должен быть выполнен до уничтожения, чтобы вы могли проверять ошибки после выполнения закрытия и сообщать о них. Однако существуют исключения, и, в частности, классы, которые переносят открытый файл, должны обычно закрывать его в своем деструкторе (без проверки ошибок, поскольку вы ничего не можете с ними сделать), чтобы обеспечить надлежащую очистку в случае исключение.

Предположительно, исключение перед закрытием означает, что уже произошла ошибка, и записываемый файл не будет использоваться. Я обычно обернуть вывод в классе с commit функция. commit закрывает и проверяет на ошибки. Если деструктор вызывается раньше commit, он закрывается, не проверяя ошибки, а затем удаляет записываемый файл, так как он, вероятно, не завершен или не может быть использован.

4

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

Глотание исключений, как правило, является «худшей практикой», потому что в первую очередь она побеждает цель броска.

Но в этом случае вам действительно нужно только подмножество функциональности в деструкторе, не включая сброс, который является «бонусом», но влечет за собой возможность броска. Попытки сброса могут по-прежнему иметь побочные эффекты, такие как ненужное ожидание тайм-аута сети, когда он уже произошел.

Как упоминал Джеймс Канз, лучшая практика заключается в ручной очистке перед запуском деструктора, что исключает исключительное состояние деструктора.

В будущем C ++ может лучше поддерживать транзакции. Но пока ваш подход разумен. В любом случае, это как деструктор std::filebuf указано на работу:

Последствия: Уничтожает объект класса basic_filebuf<charT,traits>, Вызовы close(), Если исключение происходит во время уничтожения объекта, включая вызов close()исключение поймано, но не переброшено (см. 17.6.5.12).

5

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