我正在尝试用文件流实现std :: map(实际上是std :: pair)。因为标准c ++文件流(ifstreams ofstream和fstream)不可复制,所以选择来自 stdio 的 FILE 。 这是最简单的类包装器:
#include <stdio.h>
class FileWriter
{
public:
FileWriter(const char* fileName)
{
_fs = fopen(fileName, "w");
}
~FileWriter()
{
fclose(_fs);
}
private:
FILE* _fs;
};
让我们尝试在std :: map中使用此类作为模板参数:
int main()
{
std::map<int, FileWriter> a{ { 1, FileWriter("fl.fl") } };
}
它编译得很好,但是我得到了一个运行时错误 - 内存转储。调试器显示,析构函数~FileWriter()执行两次。为什么会发生这种情况以及如何避免此错误?
答案 0 :(得分:2)
您使用临时地图创建了地图,该地图将被复制到地图中需要的位置。由于您未提供复制构造函数,因此使用默认值,只复制_fs
。调用临时的析构函数,关闭文件。然后,当地图被销毁时,将在放置在地图中的副本上再次调用析构函数。
您需要为自己的复制构造函数提供适当的语义。但是什么应该是语义? iostream因某种原因无法复制。你可以让你的班级只移动,但那时你和iostream的情况完全相同,因为它们也只是移动。
另请参阅:http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29
答案 1 :(得分:2)
您的代码的问题是复制构造函数刚刚复制了FILE*
,并且在将对象插入std::map<...>
时创建了一个临时对象。因此,当您尝试访问FILE*
时,传入的fclose()
获得FILE*
d并且您获得了未定义的行为。
在C ++ 11中,emplace()
可以流入地图:
std::map<int, std::ofstream> streams;
streams.emplace(42, std::ofstream("hello, world"));
答案 2 :(得分:1)
因为标准的c ++文件流(ifstreams ofstream和fstream)不可复制。
是的,因为复制流没有意义。
流不是容器;它们是数据流。
不要试图解决这个问题。
答案 3 :(得分:0)
构造FileWriter类,然后将其复制到地图中(复制构造)。这就是析构函数被调用两次的原因。