2017-02-15 33 views
1

我有一个ofstream的地图。我正在浏览一个消息包,并且希望将与特定符号有关的每条消息写入其各自的文件,实质上是将包文件分成更小的文件。当我<<ofstream时它会写入文件。但是,当我尝试<<到通过映射迭代器访问的ofstream时,它即使编译好也会崩溃。如何写入通过地图迭代器访问的ofstream

我使用映射的原因是为了确保数据包标头不会多次写入给定的符号。

void write_packet_to_symbol_file(packet p) 
{ 

    string path = "E:\\20170131\\"; 
    map<string,ofstream&> outs; 
    for (message m : p.messages) { 
    map<string,ofstream&>::iterator it = outs.find(m.symbol.name); 
    if (it == outs.end()) { 
     string full_path = path + m.symbol.name + ".CAP"; 
     ofstream of; 
     of.open(full_path, ios_base::app); 
     // write packet header since first message for that symbol 
     of << p.get_header(); 
     outs.emplace(m.symbol.name,of); 
    } 

    map<string,ofstream&>::iterator it2 = outs.find(m.symbol.name); 
    if (it2 != outs.end()) 
     it2->second << m.get_message_content(); 
} 

我在做什么错?

+1

如果找不到名称,则会打开一个新流并将其添加到地图中。但你永远不会指定它来指向它。 – Barmar

+1

你做错了两件事:你假设只是因为程序编译,这是正确的。这显然不是真的。其次,流对象不可复制,所以你不能构造和打开流对象,然后将它们复制到地图中。它们是可移动的,但您需要正确移动流对象,才能正常工作。 –

+0

将不正确的新节点插入到映射中后,您要遵守的迭代器“it”仍等于outs.end()。 – felix

回答

6

您的ofstream实例是if语句的局部变量,当if语句终止时将被关闭并丢弃。你几乎不想使用像你的地图这样的引用集合,而应该创建一个字符串映射到流指针(最好是智能指针),动态创建流,并且取消你已经编写的所有代码。

+0

@Neil Butterworth如果将在if块的末尾丢弃它,指针如何持续存在? – bkarj

+1

@Beh指针不会持续它;用'new'创建它将会持续它。另外,您可以使用移动语义,但不能使用引用地图。 –

+0

无论是指针还是移动语义都不需要做这项工作。看到我的答案。 – zett42

0

您似乎有一个map从字符串到流引用。我不知道如何编译,但这绝对是一个错误,因为引用的对象被破坏。

我认为你应该使用map<string,ofstream> 那么你应该.insert().emplace()地图,然后在返回的迭代,你应该叫it->second.open()

因为ifstream不复制,但是可以移动,

map.insert(make_pair(some_key, ifstream(...))); //this will work, calls move 

ifstream x; 
map.insert(make_pair(some_key, x)); //this will NOT work, calls copy 

map.insert(make_pair(some_key, std::move(x))); // should work 

类似的是emplace,即跳过make_pair通话这可能是有点棘手。

+2

它被编译,因为它在语法上是正确的。编译器为你做的任何事情,比如警告,都是肉汁。从正确的语法不能假定逻辑是正确的。我举了一些榜样的政客为例。 – user4581301

1

正如其他人已经解释过的,你只是在if块内创建临时的流对象。当if块结束时,该对象被破坏,因此存储在地图中的引用将无效。

你想要一个map<string,ofstream>,因为地图实际上应该存储流对象,而不仅仅是对它的引用。

现在我们遇到了问题,如何将临时流导入地图?复制流对象是不可能的,因为流没有复制构造函数和赋值运算符。

事实证明,临时对象不是必需的,因为您可以使用运算符[]直接在地图内部创建流对象。

因此,您甚至不需要像某些用户建议的指针或移动语义!

void write_packet_to_symbol_file(packet p) 
{ 

    string path = "E:\\20170131\\"; 
    map<string,ofstream> outs; 
    for (message m : p.messages) { 
    map<string,ofstream>::iterator it = outs.find(m.symbol.name); 
    if (it == outs.end()) { 
     // Create stream object in the map and get reference 'of' to it 
     ofstream& of = outs[ m.symbol.name ]; 

     string full_path = path + m.symbol.name + ".CAP"; 
     of.open(full_path, ios_base::app); 
     // write packet header since first message for that symbol 
     of << p.get_header(); 

     // No need to insert 'of' into 'outs', because it is already in there! 
    } 

    map<string,ofstream>::iterator it2 = outs.find(m.symbol.name); 
    if (it2 != outs.end()) 
     it2->second << m.get_message_content(); 
}