Czy muszę ręcznie zamykać ifstream?

201

Czy muszę ręcznie dzwonić, close()gdy używamstd::ifstream ?

Na przykład w kodzie:

std::string readContentsOfFile(std::string fileName) {

  std::ifstream file(fileName.c_str());

  if (file.good()) {
      std::stringstream buffer;
      buffer << file.rdbuf();
      file.close();

      return buffer.str();
  }
  throw std::runtime_exception("file not found");
}

Czy muszę dzwonić file.close()ręcznie? Czy nie należy ifstreamużywać RAII do zamykania plików?

Edison Gustavo Muenz
źródło

Odpowiedzi:

251

NIE

Po to jest RAII, niech destruktor wykona swoją pracę. Ręczne zamykanie nie szkodzi, ale to nie jest sposób w C ++, programuje się w C z klasami.

Jeśli chcesz zamknąć plik przed końcem funkcji, zawsze możesz użyć zagnieżdżonego zakresu.

W standardzie (27.8.1.5 Szablon klasy basic_ifstream) ifstreamnależy zaimplementować basic_filebufelement posiadający rzeczywisty uchwyt pliku. Jest on utrzymywany jako element członkowski, więc gdy obiekt ifstream ulega zniszczeniu, wywołuje również destruktor basic_filebuf. I ze standardu (27.8.1.2) ten destruktor zamyka plik:

virtual ˜basic_filebuf();

Efekty: Niszczy obiekt klasy basic_filebuf<charT,traits>. Połączenia close().

Zaćmienie
źródło
4
+1 Nie wiedziałem, że RAII radzi sobie z tym ... Myślę, że uczysz się czegoś nowego
każdego dnia
21
Używanie zagnieżdżonego zakresu tylko do zamykania pliku jest całkowicie sztuczne - jeśli chcesz go zamknąć, wywołaj na nim close ().
3
Chociaż możesz argumentować, że ograniczenie czasu życia obiektu do niezbędnego zakresu oznacza, że ​​przypadkowo nie uzyskasz dostępu do zamkniętego strumienia ifstream. Ale to trochę wymyślone.
Zaćmienie
9
W C ++ zagnieżdżone zakresy prawie nigdy nie są niepotrzebne. Mają wszystko do czynienia z zachowaniem kodu, zwłaszcza gdy coś się rzuca. Jeśli przyszły opiekun je usunie, nie zna dobrze C ++.
Elliot Cameron
2
Czasami musisz zadzwonić close()ręcznie w celu obsługi błędów.
ks1322,
71

Czy musisz zamknąć plik?
NIE

Czy powinieneś zamknąć plik?
Zależy.

Czy zależy Ci na możliwych warunkach błędu, które mogą wystąpić, jeśli plik nie zostanie poprawnie zamknięty? Pamiętaj o zamkniętych połączeniach, setstate(failbit)jeśli się nie powiedzie. Destruktor zadzwoni close()do ciebie automatycznie z powodu RAII, ale nie pozostawi Ci możliwości przetestowania bitu niepowodzenia, ponieważ obiekt już nie istnieje.

Martin York
źródło
14

Zgadzam się z @Martin. Jeśli piszesz do pliku, dane mogą nadal znajdować się w buforze i mogą nie zostać zapisane do pliku, dopóki nie close()zostanie wywołane. Bez zrobienia tego ręcznie nie masz pojęcia, czy wystąpił błąd, czy nie. Niezgłaszanie błędów użytkownikowi jest bardzo złą praktyką.

Mark Edwards
źródło
5

Nie, robi to automatycznie ifstreamdestruktor. Jedynym powodem, dla którego powinieneś wywołać go ręcznie, jest to, że fstreaminstancja ma duży zasięg, na przykład, jeśli jest zmienną składową instancji klasy długo żyjącej.

Dimitri C.
źródło
4
Innym powodem może być sprawdzenie błędów zamykania plików i zapobieganie rzucaniu destruktora, jeśli ze strumieniem dozwolone są wyjątki.
Daniel Langr,
4

Możesz pozwolić destruktorowi wykonać swoją pracę. Ale podobnie jak każdy obiekt RAII może się zdarzyć, że ręczne wywołanie metody close może mieć znaczenie. Na przykład:

#include <fstream>

using std::ofstream;

int main() {
  ofstream ofs("hello.txt");
  ofs << "Hello world\n";
  return 0;
}

zapisuje zawartość pliku. Ale:

#include <stdlib.h>

#include <fstream>

using std::ofstream;

int main() {
  ofstream ofs("hello.txt");
  ofs << "Hello world\n";
  exit(0);
}

nie. Są to rzadkie przypadki, gdy proces nagle kończy się. Awaria procesu może zrobić podobnie.

ericcurtin
źródło