2017-02-15 63 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(); 
}