簡體   English   中英

將大對象放在堆上的最佳方法是什么?

[英]What is the best way to put large objects on the heap?

我正在開發一個項目,需要從數據文件中加載許多對象並將它們存儲在內存中。 因為我被告知堆棧空間很少,並且堆上的數據量應該更多,所以我將所有內容放在堆上。 但是,我的印象是我過度了一點。

我目前的設計如下:

class RoadMap
{
    unique_ptr<set<unique_ptr<Node>>> allNodes;

    void addNode(unique_ptr<Node> node)
    {
        this->allNodes->insert(std::move(node));
    }
}

int main()
{
    unique_ptr<RoadMap> map(new RoadMap());

    // open file etc.

    for (auto nodeData : nodesInFile)
    {
        map->addNode(unique_ptr<Node>(new Node(nodeData)));
    }
}

從我現在所理解的,這會產生很多開銷,因為我認為我不需要涉及許多獨特的指針。 如果我理解正確,那么在“指針鏈”中只有一個唯一的指針屏障就足夠了。 但是,我不確定這樣做的最佳做法是什么。

選項1

class RoadMap
{
    unique_ptr<set<Node>> allNodes;

    void addNode (Node node)
    {
        this->allNodes->insert(node);
    }
}

int main()
{
    RoadMap map;
    //open file etc.
    for (auto nodeData : nodesInFile)
    {
        map.addNode(Node(nodeData));
    }
}

這樣做的好處在於, RoadMap類本身是唯一需要處理堆分配的類,並且在創建set時只執行一次。

選項2

class RoadMap
{
    set<Node> allNodes;

    void addNode (Node node)
    {
        this->allNodes.insert(node);
    }
}

int main()
{
    unique_ptr<RoadMap> map(new RoadMap());
    // open file etc.
    for (auto nodeData : nodesInFile)
    {
        map->addNode(Node(nodeData));
    }
}

這里唯一的指針只在main函數中,這意味着RoadMap類的用戶需要知道這個對象可以變得非常大並且應該放在堆棧中。 我不認為這是一個非常好的解決方案。

選項3

class RoadMap
{
    set<unique_ptr<Node>> allNodes;

    void addNode(unique_ptr<Node> node)
    {
        this->allNodes.insert(std::move(node));
    {
}

int main()
{
    RoadMap map;
    // open file etc.
    for (auto nodeData : nodesInFile)
    {
        map.addNode(unique_ptr<Node>(new Node(nodeData)));
    }
}

此解決方案使用許多唯一指針,這意味着在刪除RoadMap時,需要RoadMap許多析構函數和delete s。 此外, RoadMap調用者在添加節點時必須提供unique_ptr ,這意味着他必須自己進行堆分配。


現在,我贊成選項1而不是其他選項。 但是,我只是在相對較短的時間內編寫了C ++,並且不確定我是否完全理解了內存管理背后的概念,這就是為什么我希望你(在)驗證我的觀點。 假設選項1是最好的方法,我是否正確? 對於這類事情,您是否還有其他最佳實踐參考?

Node一個移動構造函數並移動賦值運算符(使集合上的操作便宜),然后使用選項1和2的混合std::set將已經堆分配其內容,因此您不必擔心分配堆上的RoadMap 注意addNode的額外std::move以允許將Node移動到集合中。

class RoadMap
{
    set<Node> allNodes;

    void addNode (Node node)
    {
        allNodes.emplace(std::move(node));
    }
};

int main()
{
    RoadMap map;
    // open file etc.
    for (const auto& nodeData : nodesInFile)
    {
        map.addNode(Node(nodeData));
    }
}

他們每個人都彼此截然不同。

為簡單起見,我建議選項2。 但是在某些操作(如sort等)中可能會更加性能密集,因為您將移動整個Node而不是指向它的指針。

我認為這不是問題,因為你正在使用set 您仍然可以通過在Node對象上使用移動語義來優化它。 除此之外,每個添加仍然使用1個副本。

我提到的上述問題可能是vector問題。 直接存儲對象的另一個問題是缺少多態性。 你不能存儲Node子類型,它們會被切片。

如果這是一個問題我會建議選項2.存儲指針意味着移動它們更快,並且多態性工作。

我認為沒有選擇1或原始解決方案的理由。

ps你的代碼中的this->是不必要的。

pps正如DyP指出set無論如何使用堆,這使得Option 2變得更好。 線索 - 基於堆棧的結構無法增長。 =>我認為只有std::array存儲在堆棧中。

讓我談談元問題:你不希望堆棧溢出,從而將你的數據結構放在堆上。 這是正確的做法。 但是要理解的重要一點是事情將被放到堆上。

每個局部變量都在堆棧上分配。 如果您有動態大小的數據結構,那么它們會引用(最重要的)所有情況下的堆。 (我知道的唯一例外是當你使用alloca()std::get_temporary_buffer()或類似的東西保留堆棧上的內存時。 特別是所有STL容器都將它們的內存保留在堆上,並且幾乎不使用任何用於局部變量或成員變量的堆棧內存(除了std::array其大小在編譯時是已知的)。

因此,如果要保存堆棧內存,將動態大小的數據結構包裝到unique_ptrs中的效果非常小,但它會為程序增加間接性,從而使代碼復雜化,減慢執行速度並不必要地增加堆內存使用量。

下面是一個示例:在具有32位編譯的Visual Studio 2010上, std::set將在堆棧上使用20個字節的內存,與模板類型參數和集合中包含的實際數字元素無關。 set元素的內存在堆上。

我相信,您現在可以自行決定是否將unique_ptrs用於您的意圖。

基本上,它還取決於您希望如何訪問RoadMap實例中存儲的Node實例。 我假設您的Node實例將釋放包裝的筆記數據。

我會去調整版本2。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM