2013-12-20 2 views
2

У меня есть некоторый код, который выглядит немного как это:Должен ли я писать конец файла в деструкторе?

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

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

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

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

+0

Привет, пожалуйста, не редактируйте мои ответы. Я обычно знаю, что я делаю;) – Potatoswatter

+1

@Potatoswatter: Редактирование ответов прямо разрешено этим сайтом, и не совсем правильно просить, чтобы кто-то оставил вас в покое. Тем не менее, редактирование было неправильным. –

+0

@LightnessRacesinOrbit Я не согласен в принципе и на практике. Нет причин, по которым я не могу отказаться от публичных исправлений. Но это обсуждение мета. – Potatoswatter

ответ

4

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

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

5

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

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

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

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

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