簡體   English   中英

返回std :: list代價高昂?

[英]Is returning a std::list costly?

我想知道如果返回一個列表,而不是返回指向一個列表的指針,在性能方面是昂貴的,因為如果我記得,列表沒有很多屬性(不是它像3個指針一樣?一個用於當前位置,一個用於開頭,一個用於結束?)。

如果按值返回std::list ,它將不僅復制列表頭,它將復制列表中每個項目的一個列表節點。 所以,是的,對於大型清單來說,這是昂貴的。

如果列表是在返回它的函數中構建的,那么您可以從命名的返回值優化中受益,以避免不必要的復制。 但這是特定於您的編譯器的。 如果例如在調用函數之前已存在列表(例如,如果它是對象的成員變量),則它永遠不會應用。

為避免按值返回容器,C ++中常見的習慣用法是將輸出迭代器作為參數。 所以代替:

std::list<int> getListOfInts() {
    std::list<int> l;
    for (int i = 0; i < 10; ++i) {
        l.push_back(i);
    }
    return l;
}

你做:

template<typename OutputIterator>
void getInts(OutputIterator out) {
    for (int i = 0; i < 10; ++i) {
        *(out++) = i;
    }
}

然后呼叫者做:

std::list<int> l;
getInts(std::back_inserter(l));

通常,一旦編譯器完成內聯和優化,代碼或多或少相同。

這樣做的好處是調用者不依賴於特定的集合 - 例如,如果對特定情況更有用,他可以將項目添加到向量而不是列表中。 如果他只需要查看每個項目一次,而不是將它們全部放在一起,那么他可以通過使用他自己設計的輸出迭代器在流模式下處理它們來節省內存。

缺點與任何模板代碼相同:在編譯時,調用者必須可以使用實現,並且最終可以為模板的多個實例化提供大量“重復”對象代碼。 當然,您可以使用相同的模式而不使用模板,通過將函數指針(如果需要,加上用戶數據指針)作為參數並使用每個項調用一次,或者通過使用純虛擬成員定義IntVisitor抽象類函數,讓調用者提供它的實例。

[編輯:TED在評論中指出,另一種避免復制而不使用模板的方法是讓調用者通過引用傳入列表。 這肯定有效,它只是給調用者不如模板的靈活性,因此不是STL使用的習語。 如果你不想要我上面描述的“優勢”這是一個很好的選擇。 然而,STL背后的原始意圖之一是將“算法”(在這種情況下,無論什么決定值)與“容器”分開(在這種情況下,值恰好存儲在列表中,而不是到一個矢量或一個數組或一個自我排序集,或者只打印出來而不存儲它們。)

它(一如既往)取決於。 在以下代碼中,可以通過返回來調用或不調用復制構造函數。

std::list<int> foo() {
  std::list<int> bar;
  // ...
  return bar;
};

如果編譯器應用返回值優化 ,則可能不會調用它。 如果調用了復制構造函數,那么相對於較大列表的指針,它可能更昂貴,如果沒有調用它,那么返回直接列表會更快(因為它避免了動態分配)

就個人而言,我不擔心它並返回直接列表。 然后,只有當我的探查器說這個問題時我才會考慮優化。

如果按值返回,則將調用復制構造函數,並逐個復制項目。 有時你會被命名值優化保存,因為onebyone指出。

確保副本不會發生的主要選項是:

  • 通過引用傳入列表以由函數填寫。 這樣你就告訴函數把數據放在哪里,不需要復制,因為你把它放在最后的位置。
  • 在堆上分配一個列表並返回它。 你應該在一個像std :: auto_ptr或boost :: shared_ptr這樣的智能指針中返回它,以確保它被刪除並且是異常安全的。

我相信,復制構造函數被調用。

它可能是昂貴的,因為它將復制列表中的每個元素。 更重要的是,它有不同的行為:你想要一個列表的副本,還是想要一個指向原始列表的指針?

您可以編寫自己的復制構造函數,以便它不會復制。

暫無
暫無

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

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