Открыть, записать и закрыть файл

У меня есть небольшая 10-линейная функция, которая записывает некоторые данные в файл с помощью std::ofstream. В конце моей функции я явно не вызывал .close(), но он не просмотрел обзор кода по той причине, что лучше явно называть его по причинам стиля и многословности. Я понимаю, что нет никакого вреда при вызове .close() явно, но вызывает ли это явно перед тем, как оператор return указывает на отсутствие понимания или веры в RAII?

В стандарте C ++ говорится:

  

§27.8.1.2

     

virtual ~ basic_filebuf ();

     

[3] Эффекты: уничтожает объект класса class basic_filebuf<charT,traits>. Вызов close().

Как я оправдался в своем аргументе о том, что вызов .close() в конце функции является избыточным и /или ненужным?

bool SomeClass::saveData()
{
    std::ofstream saveFile(m_filename);

    if (!saveFile.is_open())
        return false;

    saveFile << m_member1 << std::endl;
    saveFile << m_member2 << std::endl;

    saveFile.close(); // passed review only with this line
    return true;
}

Функция должна возвращать false, если файл не может быть открыт для записи.

62 голоса | спросил dreamlax 2 FebruaryEurope/MoscowbWed, 02 Feb 2011 07:00:41 +0300000000amWed, 02 Feb 2011 07:00:41 +030011 2011, 07:00:41

8 ответов


65

Я бы сказал прямо противоположное.

Явное закрытие потока, вероятно, не то, что вы хотите сделать. Это связано с тем, что при close() в потоке есть вероятность того, что будут выбрасываться исключения. Таким образом, когда вы явно закрываете поток файлов, это указывает на то, что вы оба хотите закрыть поток и явно обрабатывать любые ошибки, которые могут возникнуть (исключения или неправильные биты) от закрытия потока (или, возможно, вы говорите, если это не удается хотите быстро выйти из строя (исключение разрешено убить приложение)).

Если вас не волнуют ошибки (т. е. вы все равно не будете их обрабатывать). Вы должны просто позволить деструктору сделать закрытие. Это связано с тем, что деструктор будет улавливать и отбрасывать любые исключения, тем самым обеспечивая нормальный поток кода. Когда вы занимаетесь закрытием файла, это то, что вы обычно хотите делать (если сбой закрытия не имеет значения?).

ответил Martin York 2 FebruaryEurope/MoscowbWed, 02 Feb 2011 10:51:25 +0300000000amWed, 02 Feb 2011 10:51:25 +030011 2011, 10:51:25
37

Предполагая, что объект fstream является локальным для функции, I будет склоняться против этого. Люди должны привыкнуть позволить RAII выполнять свою работу, и закрытие объекта fstream попадает под эту рубрику. Дополнительный код, который не выполняет что-то полезное, почти всегда является плохой идеей.

Изменить: Чтобы меня не поняли, я бы возразил против этого не только по этому конкретному делу, но и в целом. Это не просто бесполезно, но имеет тенденцию скрывать то, что нужно, и (что хуже всего) практически невозможно обеспечить в любом случае - люди, которые думают только о «нормальном» выходе из функции, действительно должны остановиться и понять, что в ту минуту, когда они добавили обработку исключений на C ++, правила изменились фундаментальным образом. Вам нужно думать в терминах RAII (или что-то подобное), что обеспечивает очистку при выходе из области видимости - и явно закрывает файлы, освобождая память и т. Д., Делает не .

ответил Jerry Coffin 2 FebruaryEurope/MoscowbWed, 02 Feb 2011 07:52:45 +0300000000amWed, 02 Feb 2011 07:52:45 +030011 2011, 07:52:45
16

Здесь есть место. Причина, по которой рецензенты хотят, чтобы явный close() "в качестве стиля и многословия" заключался в том, что без него они не могут просто сказать о чтении кода, если вы хотели сделать это таким образом, или если вы полностью забыли об этом и просто повезло. Также возможно, что их эго были ушиблены из-за того, что не заметили или не помнили, по крайней мере, сначала, что деструктор будет вызывать close(). Добавление комментария, которое деструктор вызывает close(), не является плохим. Это немного безвозмездно, но если ваши коллеги нуждаются в разъяснении и /или заверении сейчас, есть хорошие шансы, что случайный помощник через несколько лет будет слишком дорог, особенно если ваша команда не делает много файлов ввода /вывода.

ответил Karl Bielefeldt 2 FebruaryEurope/MoscowbWed, 02 Feb 2011 14:29:32 +0300000000pmWed, 02 Feb 2011 14:29:32 +030011 2011, 14:29:32
9

Я согласен с ответом Локи: различие между вызовом close явно и разрешение вызова деструктора закрывается, заключается в том, что деструктор будет неявно улавливать (т. е. скрывать) любое исключение, созданное закрытием.

Деструктор должен сделать это (не распространять исключения), потому что он может быть вызван, если /пока существует исключение, которое уже выбрано; и бросание второго исключения в течение первого исключения является фатальным (поэтому все деструкторы должны избегать исключения исключений).

В отличие от Loki, я хотел бы утверждать, что вы хотите явно позвонить, точно потому, что вы хотите, чтобы любое исключение было видимым. Например, возможно, данные важны, и вы хотите, чтобы они были записаны на диск; возможно, диск заполнен, оператор вывода << записывается в кэш-память в памяти, и никто не замечает, что диск заполнен до тех пор, пока тесный неявно вызывает сброс. Вам не разрешено возвращать false, поскольку false определяется как означающее, что файл не может быть открыт. ИМО - единственная разумная /безопасная вещь, которую вы можете сделать, - это исключение.

Это зависит от вызывающего, чтобы поймать любое исключение; без исключения, должна быть гарантией того, что закрытие было успешным, и данные, безопасно записанные на O /S.

ответил ChrisW 5 MaramWed, 05 Mar 2014 02:28:59 +04002014-03-05T02:28:59+04:0002 2014, 02:28:59
8

Я разорван на этом. Вы абсолютно правы. Однако, если для стандартного кодирования требуется явно вызвать close (), или это общее мнение группы людей о том, что вы это сделаете, вы не можете многое сделать. Если бы я был вами, я бы просто пошел с потоком. Рассуждение таких вещей непродуктивно.

ответил grokus 2 FebruaryEurope/MoscowbWed, 02 Feb 2011 07:22:11 +0300000000amWed, 02 Feb 2011 07:22:11 +030011 2011, 07:22:11
4

Я считаю, что вы задаете два вопроса в одном. Должны ли вы использовать исключения или возвращаемые значения? Если вы используете RAII или нет?

Если исключения не разрешены в вашей компании , тогда fstream::exceptions() должен быть установлен глобально для вашего проекта. И вы также должны допросить флаг ошибки.

Если RAII не разрешено в вашей компании , тогда не используйте C ++. Если вы используете RAII, то любое исключение, вызванное деструктором, будет проглочено. Это звучит ужасно, и это так. Однако я согласен с другими, что RAII - это путь, потому что любая обработка ошибок здесь бесполезна и создает нечитаемый код. Это связано с тем, что «флеш» не делает то, что вы можете себе представить. Он инструктирует операционную систему сделать это от вашего имени. ОС будет делать это, когда считает, что это удобно. Затем, когда рабочие сбрасывания (которые могут быть через минуту после возврата вашей функции), подобные вещи могут произойти на аппаратном уровне. На диске может быть SSD-кеш, который позже сбрасывается на вращающиеся диски (что может произойти ночью, когда он менее занят). Наконец данные заканчиваются на диске. Но история здесь не заканчивается. Возможно, данные были сохранены правильно, но диск уничтожен в зависимости от множества возможных причин. Следовательно, если RAII недостаточно безопасен для вашей транзакции, вам нужно все же перейти на более низкий уровень API, и даже это не будет идеальным. Извините за то, что вы здесь.

ответил Patrick Fromberg 24 +04002013-10-24T19:41:04+04:00312013bEurope/MoscowThu, 24 Oct 2013 19:41:04 +0400 2013, 19:41:04
1

Вам также необходимо проверить, что выполняются операции записи (<<). Поэтому вместо проверки is_open() просто выполните весь цикл операций и проверьте failbit в конце:

bool save_data() const
{
    std::ofstream saveFile(m_filename);

    saveFile << m_member1 << '\n'
             << m_member2 << '\n';

    saveFile.close(); // may set failbit

    return saveFile;
}

Кроме того, если нет необходимости нажимать каждую строку, поскольку она написана, предпочитайте использовать '\n', а не std::endl (как я ) и close() записать все за один раз - это может существенно повлиять на скорость, особенно когда есть большое количество строк для записи.

ответил Toby Speight 14 FebruaryEurope/MoscowbWed, 14 Feb 2018 13:51:37 +0300000000pmWed, 14 Feb 2018 13:51:37 +030018 2018, 13:51:37
0

Прочитав вопрос и ответы, я пришел к выводу, что это комментарии могут вступить в игру. Я недавно прочитал этот Q /A Угадывание числа, но комментарии относительно и принятый ответ дал мне представление о ситуации здесь. Используйте комментарии, чтобы объяснить, почему, пусть код объяснит, как это сделать. Я могу использовать вашу функцию аналогично для каждого случая в качестве примера:

bool SomeClass::saveData() {
    std::ofstream saveFile(m_filename);

    if (!saveFile.is_open())
        return false;

    saveFile << m_member1 << std::endl; // I would replace `endl` with `'\n'`
    saveFile << m_member2 << std::endl; // for performance reasons. 

    // I intentionally want to close the file before the closing scope to
    // free up limited resources and to log potential exceptions and errors.  
    saveFile.close(); 
    return true;
} 

bool SomeClass::saveData() {
    std::ofstream saveFile(m_filename);

    if (!saveFile.is_open())
        return false;

    saveFile << m_member1 << std::endl; // I would replace `endl` with `'\n'`
    saveFile << m_member2 << std::endl; // for performance reasons. 

    // Resources and errors are not a concern: relying on RAII no need
    // to call file.close();
    return true;
}

Тогда подходящий тип комментариев по уважительным причинам может послужить вам хорошо. Тогда, по крайней мере, так; можно было бы знать, что вы намеревались позвонить или опустить его и почему! Хорошо написанный код - это то, как и не нужно комментариев.

ответил Francis Cugler 22 MarpmThu, 22 Mar 2018 15:06:17 +03002018-03-22T15:06:17+03:0003 2018, 15:06:17

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132