我正在为FILE *编写RAII包装器。我注意到,当在析构函数中关闭后删除FILE *会导致未定义的行为(seg。错误或其他地方的错误)。我假设fclose会将FILE *设置为NULL,但事实并非如此。
class smartFP {
smartFP (const std::string& name)
: fp (fopen(name.c_str(), "r")
{ }
~smartFP()
{
if (fp) {
fclose(fp);
// delete(fp); <- This is causing crash
fp = NULL; <- Is this OK?
}
}
private:
FILE *fp;
};
答案 0 :(得分:14)
当然delete fp
导致崩溃。它没有分配new
。只能使用delete
或使用文档告诉您使用的其他内容来调用new
。 fopen
的文档从未告诉您使用delete
。文件的所有清理都由fclose
执行;在调用文件相关资源之后,您无需执行任何其他操作。
设置fp = NULL
很好。可能需要这样的“智能文件指针”的未来消费者可以检查指针是否仍然有效。 (但是从reset
方法比从析构函数更有用;在析构函数运行后,指针类的任何未来消费者都不会因为该对象不再存在而存在。)但是{{1因为fclose
没有通过引用接收它的参数,所以它本身不能这样做,即使它没有,它也无法使所有可能的文件副本无效指针。回想一下,fclose
和free
也不会将其参数设置为delete
。
答案 1 :(得分:2)
不,你不应该尝试delete
一个文件*。它是一个C库数据结构,并不代表从C ++ new
返回的指针。
答案 2 :(得分:2)
fp不是由C运行时分配的,您不必释放它。 fclose不会将其设置为NULL,因为它无法将其设置为null(它是指向FILE结构的指针,而不是指向FILE *)。
如何分配FP并不是您关心的问题,它不是您与API合约的一部分,所以不要担心。
附录:
fopen返回一个指向FILE结构的指针。它在何处以及如何获得记忆并不重要。它很可能指向内存中的静态结构。但是,基本上,你不应该对那个记忆负责,所以你不应该搞乱它。
现在,实际的fp指针,你可以分配:
FILE **fp = malloc(sizeof(FILE *));
*fp = fopen("file.txt", "r");
...
fclose(*fp);
free(fp);
但是,显然,大多数人不这样做,他们只是使用本地堆栈变量来管理它。取决于用例。
答案 3 :(得分:1)
fopen
和fclose
使用FILE *
元素进行必要的动态内存管理,因此您无需在其上调用delete
。但是,如果需要,可以在其中添加NULL值,以表示未分配文件。
答案 4 :(得分:1)
1)fclose 无法将FILE *设置为NULL,因为它的参数是FILE *,因此它按值收到FILE *。由于它是一个副本,它不能改变你的指针,除非参数被改为FILE *&amp;。
2)在C ++中,除非你调用delete
,否则几乎不会调用new
。如果您正在进行RAII,则只应将new
分配给智能指针,并且永远不要再调用delete
。