2013-02-08 31 views
3

我花了一些時間尋找答案,但沒有找到任何令人滿意的東西。C++:替代矢量引用以避免複製大數據

只是對一些經驗豐富的C++人如何解決這類問題感興趣,因爲現在我正在做的是比原型更多的與生產相關的編碼。

假設你有一個擁有大量數據(比如說500Mb)的unordered_map(hashmap)的類。你想寫一個訪問器,以有效的方式返回這些數據的一部分。

進行以下操作,其中BigData是一些存儲適量數據的類。

Class A 
{ 
    private: 
     unordered_map<string, BigData> m_map; // lots of data 

    public: 

    vector<BigData> get10BestItems() 
    { 
     vector<BigData> results; 
     for (........ // iterate over m_map and add 10 best items to results 
     // ... 
     return results; 
    } 

}; 

訪問者get10BestItems因爲它首先複製項的結果矢量,然後將結果矢量時該函數返回(從功能堆複製)複製不在此代碼非常有效的。

你不能在C__因各種原因引用的一個載體,這將是顯而易見的答案:

vector<BigData&> results;  // vector can't contain references. 

您可以創建堆上的結果向量,並傳遞給一個參考:

vector<BigData>& get10BestItems() // returns a reference to the vector 
    { 
     vector<BigData> results = new vector<BigData>; // generate on heap 
     for (........ // iterate over m_map and add 10 best items to results 
      // ... 
     return results; // can return the reference 
    } 

但是,如果你不小心,你會遇到內存泄漏問題。它也很慢(堆內存),並仍然將數據從地圖複製到矢量。

因此,我們可以在C風格的編碼回頭,只是使用指針:

vector<BigData*> get10BestItems() // returns a vector of pointers 
    { 
     vector<BigData*> results ; // vectors of pointers 
     for (........ // iterate over m_map and add 10 best items to results 
     // ... 
     return results; 
    } 

但是大多數的消息來源說,不使用指針,除非絕對必要。有些選項可以使用smart_pointers和boost ptr_vector,但我寧願儘量避免這些。

我不認爲地圖會變成靜態的,所以我不太擔心壞指針。只有一個問題,如果代碼將不得不處理指針。在風格上,這是不愉快的:

const BigData& getTheBestItem() // returns a const reference 
{ 
     string bestID; 
     for (........ // iterate over m_map, find bestID 
     // ... 
     return m_map[bestID] ; // return a referencr to the best item 
} 


vector<BigData*> get10BestItems() // returns a vector of pointers 
{  
     vector<BigData*> results ; // vectors of pointers 
     for_each ........ // iterate over m_map and add 10 best items to results 
     // ... 
     return results; 
}; 

例如,如果你想單個項目,那麼很容易返回一個引用。

最後的選擇是簡單地讓哈希地圖公共和返回鍵的矢量(在這種情況下字符串):

Class A 
{ 
     public: 

     unordered_map<string, BigData> m_map; // lots of data 



    vector<string> get10BestItemKeys() 
    { 
     vector<string> results; 
     for (........ // iterate over m_map and add 10 best KEYS to results 
     // ... 
     return results; 
    } 

}; 



A aTest; 
... // load data to map 

vector <string> best10 = aTest.get10BestItemKeys(); 
for (.... // iterate over all KEYs in best10 
{ 
    aTest.m_map.find(KEY); // do something with item. 
    // ... 
} 

什麼是最好的解決辦法嗎?速度很重要,但我希望能夠輕鬆開發和安全的編程實踐。

+1

「爲了避免複製大數據」 - 那麼可能是一個**引用**引用向量? – 2013-02-08 17:46:13

+0

這與您的問題沒有直接關係,但正如您所說的,get10BestItems()並不是非常有效,我相信您可以將時間複雜度降低到$ O(K)+ O(N \ log K)$和空間複雜度爲$ O(K)$,其中$ N $是元素的數量,$ K = 10 $。您可以使用堆來保留10個最佳值,並使用比較函數來比較兩個不同的值。你可以在這裏看到我的意思的實現:https://github.com/lakshayg/collections/blob/master/algos/ksmallest.cpp – 2016-07-01 08:46:56

回答

2

我會做同樣的事情到以下幾點:

Class A 
{ 
private: 
    unordered_map<string, BigData> m_map; // lots of data 
    vector<BigData*> best10; 

public: 
    A() 
     : best10(10) 
    { 
     // Other constructor stuff 
    } 

    const vector<BigData*>& get10BestItems() 
    { 
     // Set best10[0] through best10[9] with the pointers to the best 10 
     return best10; 
    } 

}; 

注意的幾件事情:

  • 的載體沒有被每一次重新分配,並返回一個恆定的參考,因此撥打get10BestItems時不會分配或複製任何內容。

  • 指針在這種情況下很好。您閱讀有關避免指針的事情可能與堆分配有關,現在首選std::unique_ptrstd::shared_ptr

+0

非常快速的答覆,謝謝你。似乎是在這種情況下指針不太邪惡的共識。 – user1978816 2013-02-08 18:03:06

3

如果地圖是恆定的,我只需要一個指針向量。如果你想避免數據被改變,你總是可以返回const指針。

參考文獻很適合他們工作,但有一個原因,我們仍然有指針(對我來說這將屬於'必要'類別)。

1

對我來說聽起來像是一份boost::ref的工作。只需稍微改變你原來的代碼:

typedef std::vector<boost::ref<BigData> > BestItems; 

BestItems get10BestItems() 
    { 
     BestItems results; 
     for (........ // iterate over m_map and add 10 best items to results 
     // ... 
     return results; 
    } 

現在你只理論上返回到你的回報載體內的每個項目的引用使得小型和廉價的複製(如果編譯器不能優化掉回完全複製)。

+0

感謝您的快速回復。我會研究boost:ref,它是C++ 11的一部分嗎? – user1978816 2013-02-08 18:06:17

0

我通常使用boost::range,我發現它在許多情況下是非常有用的,尤其是你描述的那種情況。

可以保留的範圍對象,並遍歷它,等

但我要指出,我不知道你是否添加/當你的範圍內,當你使用它之間去除對象會發生什麼,所以你可能想在使用它之前檢查一下。