我需要关闭一个std::fstream吗?

86

可能是重复问题:
我需要手动关闭 ifstream 吗?

我是否需要调用fstream.close()方法,或者fstream是一个合适的RAII对象,在销毁时会自动关闭流?

我在一个方法中有一个本地的std::ofstream对象。如果在退出这个方法后没有调用close方法,我能否假设文件总是被关闭了呢?我没有找到析构函数的文档。


是的,这是一个重复的内容。谢谢。我没有找到它。 - Tobias Langner
4
不是完全相同的副本。引用的问题具体涉及ifstream,而这个问题通常涉及fstream。 - Adrian McCarthy
1
“我需要关闭std::fstream吗?”在回答中说:“8年前已经关闭了。” - kristjan
3个回答

140

我认为之前的回答是误导性的。

fstream 是一个适当的 RAII 对象,它在作用域结束时会自动关闭,完全没有必要在作用域结束时手动调用 close

特别地,这不是一种“最佳实践”,也不需要刷新输出流。

而且,虽然 Drakosha 是正确的,调用 close 可以让你检查流的失败位,但是无论如何都没有人这样做。

在理想情况下,你只需事先调用 stream.exceptions(ios::failbit),并在 fstream 的析构函数中处理抛出的异常即可。但不幸的是,在 C++ 中析构函数中的异常是一个错误的概念,所以这不是一个好主意。

所以,如果你想检查文件关闭的成功与否,那就手动检查(但仅限于此时)。


7
我每次都会仔细检查关闭的结果,因为我正在一款需要确保数据已经写入磁盘的应用上工作。另外,有一点不太相关的是,在 Linux 上,关闭并不能保证刷新(至少在 Linux 上),而且我也不确定析构函数是否会刷新。实际上,我会建议将刷新和关闭视为“最佳实践”,并且在两者上都检查错误。 - Drakosha
10
@Drakosha:确保数据被完全刷入磁盘与flushclose所保证的完全不同。通常最好的做法是假设flush将数据从进程中传输到操作系统层面,因此即使杀死该进程也不会阻止数据被写入。由于C++标准没有持久存储的概念,并将其作为(部分)文件系统呈现给出,因此没有可移植的API来确保数据已提交到持久性存储。 - Steve Jessop
2
我也对Linux的这种行为感到有些惊讶。fstream析构函数需要调用close成员函数,close需要“像fclose一样”关闭文件,而fclose被定义为刷新流。因此,析构函数当然应该刷新,尽管如果存在机器插头被拔出的风险,则刷新可能不会产生你想要的效果。你需要使用(非标准C++的)fsync来解决这个问题。 - Steve Jessop
4
在编程中手动执行本应自动完成的任务绝对是一种不好的习惯。在这种特定情况下,它似乎没有坏处(除了增加毫无意义的代码行数),但遵循这个原则并不是一个好主意。 - Konrad Rudolph
3
@jrh 在外部存储介质上关闭文件后,该介质已经被断开。这将导致关闭(和刷新)失败,但应用程序仍然可以成功继续运行。 - Konrad Rudolph
显示剩余8条评论

8
为了补充Amy Lee的回答,最好手动完成,因为这样您可以检查错误。
顺便提一下,根据"close" manpage:
未检查close()的返回值是常见但严重的编程错误。先前write(2)操作上的错误可能首次在最终的close()报告。关闭文件时不检查返回值可能导致数据的静默丢失。这在NFS和磁盘配额中尤其明显。
成功的关闭并不保证数据已成功保存到磁盘上,因为内核推迟写入。通常情况下,当流关闭时,文件系统不会刷新缓冲区。如果需要确保数据已物理存储,请使用fsync(2)。(这将取决于磁盘硬件此时的状态。)

4
如果你没有检查错误,那就意味着你误导用户数据已经在磁盘上,而实际上并非如此。 - Drakosha
5
@GMan:这并不完全取决于你,它取决于上下文。至少你应该以某种方式让用户知道。 - Drakosha
10
这个回答很糟糕。close() 不是 fstream::close()。根据规范,后者始终会刷新缓冲区。在网上很容易找到相关信息。 - cdunn2001
1
这个答案是错误的。没有必要关闭()。这里的一切都不正确。 - ABCD
1
@jrh,你做出了合理的区分。你想要确保实际检查了底层系统调用的返回值。是的,这很重要。所以我去看了源代码。答案是“是的”;事实上,fclose()的返回值被检查了,并且失败会有效地弹出调用堆栈并在适当的位置转换为流故障。因此,fstream::close()是完全安全的! - cdunn2001
显示剩余4条评论

1

我认为关闭fstream是一个好的习惯,因为你需要刷新缓冲区,这就是我听说的。


5
如果fstream是一个RAII对象,它将被关闭,从而刷新缓冲区。主要问题是是否需要在所有控制流上进行错误处理以确保它被刷新。 - Tobias Langner
4
抱歉,-1的评分是因为它某种方式得到了+2。手动释放资源要么是糟糕编程的标志,要么是对容器用途的误解。 - GManNickG
1
@GMan:我认为不关心文件是否正确关闭是个例外情况,在大多数情况下,你应该自己调用close()。太多的软件开发人员对用户文件系统的可靠性和可用性做出了我不希望他们做出的假设。 - Nick Bastin
@GManNickG 请查看我在另一个答案中的评论;任何具有向可能不可靠或可移动介质(例如闪存驱动器、网络驱动器)写入选项的程序都应该手动关闭,以便您可以通知用户保存失败和/或让他们选择其他位置。 - jrh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接