C++ 如何对通过映射迭代器访问的流进行写入

C++ 如何对通过映射迭代器访问的流进行写入,c++,c++11,C++,C++11,我有一张流的地图。我正在浏览一个消息包,并希望将每个与特定符号相关的消息写入其各自的文件,本质上是将该包文件划分为更小的文件。当I您的ofstream实例是if语句的局部变量时,它会写入文件,并且当if语句终止时,它将被关闭并丢弃。您几乎不想使用像map这样的引用集合,相反,您应该创建一个字符串到of stream指针(最好是到智能指针)的映射,动态创建of stream,并废弃您已经编写的所有代码。您似乎有一个从字符串到of stream reference的映射。我不确定它是如何编译的,但这

我有一张流的
地图。我正在浏览一个消息包,并希望将每个与特定符号相关的消息写入其各自的文件,本质上是将该包文件划分为更小的文件。当I
您的ofstream实例是if语句的局部变量时,它会写入文件,并且当if语句终止时,它将被关闭并丢弃。您几乎不想使用像map这样的引用集合,相反,您应该创建一个字符串到of stream指针(最好是到智能指针)的映射,动态创建of stream,并废弃您已经编写的所有代码。

您似乎有一个从字符串到of stream reference
映射。我不确定它是如何编译的,但这肯定是一个bug,因为引用的对象被破坏了

我认为你应该使用
map
然后您应该
.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
调用。

正如其他人已经解释过的,您只在if块内创建了一个临时的ofstream对象。当if块结束时,此对象将被销毁,因此存储在映射中的引用将无效

您需要一个
映射
,因为映射实际上应该存储流对象,而不仅仅是对它的引用

现在我们有一个问题,如何将临时流放到地图中?复制流对象是不可能的,因为流没有复制构造函数或赋值运算符

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

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

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();
}
void write_packet_到_symbol_文件(packet p)
{
字符串路径=“E:\\20170131\\”;
规划图;
对于(消息m:p.messages){
迭代器it=out.find(m.symbol.name);
if(it==outs.end()){
//在地图中创建流对象并获取对其的引用
ofstream&of=out[m.symbol.name];
字符串full_path=path+m.symbol.name+“.CAP”;
of.open(完整路径,ios\u base::app);
//从该符号的第一条消息开始写入数据包头

第二,当找不到名称时,您打开一个新流并将其添加到映射中。但您从未指定
it
指向它。您做了两件错误的事:您假设仅因为程序编译,它是正确的。这显然不是真的。第二,流对象不可复制,因此您无法构造和打开流对象,然后将它们复制到映射中。它们是可移动的,但您需要正确地移动流对象,这样才能工作。在将新节点错误地插入映射中后,您要遵循的迭代器
it
仍然等于out。end().@Barmar我做了一个新的迭代器,它不再崩溃。但是它没有将消息内容写入文件。@Sam Varsharvchik请详细说明移动流对象properly@NeilButterworth if将在if块末尾被丢弃指针如何保持它?@Beh指针不会保持它;使用
new将保留它。此外,您可以使用移动语义,但不能与引用映射一起使用。这不需要指针或移动语义。请参阅我的答案。编译它是因为它在语法上是正确的。编译器为您执行的任何其他操作(如警告)都是非常重要的。不能从正确的语法中假定逻辑是正确的我举政治家为例。
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();
}