2017-06-19 73 views
2

我有一個類是一個容器的委託並在內部存儲一個迭代器到這個容器。從原始容器鏡像迭代器到它的副本

class A { 
public: 
    list<int> m_data; 
    list<int>::iterator m_relevantDataStart; 

    A(const A & cpy) { 
     m_data = cpy.m_data; 
     m_relevantDataStart = cpy.m_relevantDataStart; //<--- UNWISE 
    } 
}; 

現在的問題是,如果我嘗試寫一個簡單的構造函數如上所描繪複製兩個容器和迭代器,迭代器成爲副本的情況下無法使用,更具體地講,我以後再遇到一個運行時異常試圖進行比較時:

`if(m_relevantDataStart == m_data.begin())` - Expression: list iterators incompatible 

這我相信的出現是由於m_relevantDataStart仍然是我複製的,而m_data.begin()指向原始容器的副本之類的m_data迭代器。

我發現this answer,這似乎有一些相關性,這意味着指向原始容器的iterator確實無法使用。

我的問題和TL; DR:有沒有一種方法可以將迭代器鏡像到原始容器,以便此「鏡像」的結果將指向複製容器中的對應元素?

我能想到的一個解決方案,就需要在原來的容器確定項目指標和推進在副本容器中的迭代器(線性與std::list打交道時的複雜性),但除非我用一些隨機存取容器,而不是std::list它似乎相當低效。

也總是有選擇寫一個自定義容器複製算法,我真的很想避免。

回答

4

我沒有看到太多辦法避免從副本的開始處開始,並推進迭代器直到達到所需點(只要您使用std::list)。

如果您要自行復制列表,可以將該步驟合併到原始列表中,並在迭代器到達原始列表時保存正確的迭代器。

否則,複製列表,然後提前一個迭代器在新的列表中的所需數量的地方:

A(const A & cpy) { 
    m_data = cpy.m_data; 
    auto walker = cpy.m_data.begin(); 
    m_relevantDataStart = m_data.begin(); 
    while (walker != cpy.m_relevantDataStart) { 
     ++walker; 
     ++m_relevantDataStart; 
    } 
} 

當然,你可以「隱藏」的複雜一點,通過使用std::distance找到距離從原始列表中的迭代器開始,然後用std::advance(或std::next)將迭代器移動到新距離 - 實際上,對於生產代碼,這顯然更可取;上面的代碼只是顯示了實際將要發生的事情)「)。

雖然這顯然確實具有線性複雜性,除非您正在處理真正的大型列表,它可能不會像最初顯示的那樣增加近似的執行時間。由於您剛剛完成了整個列表的逐個節點的副本,因此原始列表和剛剛創建的副本的數據(至少大部分)通常都在緩存中,因此只能遍歷它們需要從緩存中讀取(而複製步驟更可能需要從主內存中讀取大部分數據)。

如果你正在處理那些(甚至可能)足夠大以至於整個東西可能不適合緩存的列表,那麼第二次遍歷不會很便宜,你可能會考慮以兩部分進行拷貝,然後拼接拼在一起:

auto m_data = std::list(cpy.m_data.begin(), cpy.m_relevantDataStart); 
auto temp = std::list(cpy.m_relevantDataStart, cpy.m_data.end()); 
m_relevantDataStart = temp.begin(); 
m_data.splice(m_data.end(), temp); 

鑑於m_listtemp將使用相同的分配器,接頭將有不斷的複雜性和迭代器都將在整個拼接仍然有效。當然,如果你要從list切換到vector,這將會使所有的(複製和獲得正確的迭代器)使用更少的資源(但是你還沒有足夠的關於你的其他用途來猜測你可以從其他地方獲得多少利用列表而不是向量或雙向鏈表)。

+0

謝謝你的回答。在我的情況中,'list'是最合適的容器,因爲兩端的插入和刪除非常頻繁。 「距離」和「高級」解決方案是我計劃採用的解決方案。我希望可能會有一些'list'函數的隱藏過載,它會爲我做很髒的工作(可能在複製時),但是您向我保證沒有真正優雅的解決方案。 – user35443

+0

@ user35443:如果你需要在兩端插入/刪除(但不在中間),你可能需要'std :: deque'而不是'std :: list'。它在兩端提供了不斷複雜的插入/刪除操作,*和*隨機訪問迭代器。 –

+0

對不起,我的意思是(插入)和(清除兩端):) – user35443