[英]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)));
}
}
從我現在所理解的,這會產生很多開銷,因為我認為我不需要涉及許多獨特的指針。 如果我理解正確,那么在“指針鏈”中只有一個唯一的指針屏障就足夠了。 但是,我不確定這樣做的最佳做法是什么。
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
時只執行一次。
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
類的用戶需要知道這個對象可以變得非常大並且應該放在堆棧中。 我不認為這是一個非常好的解決方案。
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.