C++ 如果存储指针,std::list是否比std::vector更好?
我通常避免使用std::list,但在存储指针的情况下,使用std::list是否更有利,因为这样我就可以随机插入指针而不必移动所有其他指针?与C++ 如果存储指针,std::list是否比std::vector更好?,c++,stl,C++,Stl,我通常避免使用std::list,但在存储指针的情况下,使用std::list是否更有利,因为这样我就可以随机插入指针而不必移动所有其他指针?与std::vector 谢谢因为指针的复制构造很简单,所以这里的决定不是一个或另一个更适合存储指针,而是哪个更适合您的需要 如果你真的需要做大量的随机插入(和删除?),那么列表可以更好地工作,尽管这不是一个简单的决定-也许带保留空间的向量会更好,即使这样。是否希望列表的每个节点开销仅用于存储指针?在32位Windows上,列表中的每个条目有12个字节,加
std::vector
谢谢因为指针的复制构造很简单,所以这里的决定不是一个或另一个更适合存储指针,而是哪个更适合您的需要 如果你真的需要做大量的随机插入(和删除?),那么
列表
可以更好地工作,尽管这不是一个简单的决定-也许带保留空间的向量
会更好,即使这样。是否希望列表的每个节点开销仅用于存储指针?在32位Windows上,列表中的每个条目有12个字节,加上堆管理开销,每个条目总共有20多个字节。随着时间的推移,这也无助于数据的局部性。同时,vector
每个条目使用四个字节(同样是32位),并保证将其元素存储在连续块中
无论哪种方式,您都必须处理容器销毁时的内存清理,除非您将指针元素包装为某种智能指针。另一种方法是简化某些*
的内存管理
有关列表
、向量
和deque
的一些分析,请参见。就我个人而言,我越来越认为list
在主流中没有那么大用处。一般来说,你的第一选择应该是vector
——同样,当你存储指针时
将这个决定与C(-ish)中的这个问题进行比较:我什么时候选择
结构链接项{
要素*项目;
链接的项目*下一步;
};
结束
元素*项[];/*指向项的指针数组*/
?
有一些情况,但首先检查您是否可以应用“更简单”的数据结构,即向量
我认为它是特别是指针,在使用std::vector
而不是std::list
的情况下,即使在随机位置有许多插入/删除操作,也应该考虑使用std::vector
。指针的复制成本非常低(事实上,我希望我的标准库实现使用CPU内部函数来实现这一点),因此std::vector
数据的更高局部性可能远远超过std::list
对于随机插入/删除的理论上更好的适用性
无论如何,您必须测量您的应用程序(而不是人工测试用例)才能确定 通常,当您需要一个容器来存储某个内容,然后随机删除它时,最好是一个集合
。要删除某些内容,您需要首先找到它,对于向量和列表是O(n)
,对于set是O(log(n))
。如果找到,则立即删除列表,但向量为O(n)
,集合为O(log(n))
因此:
- 如果您很少使用向量,请考虑
vector
从中搜索或删除
容器李>
- 如果您很少在容器中搜索,请考虑
列表
李>
- 否则,请考虑设置
vector
是最高效的内存和缓存。如果您知道元素的数量,那么使用reserve()
可以使其100%的内存效率。基于基准点研究,我更喜欢使用vector,除非有其他强大的用例支持链表(带有一些开销信息)。但要以更好的方式处理(平均而言)插入和移除效率低下的问题
我的想法是:
正在删除现有的SomeObject元素:
使用线性搜索时间处理向量的现有SomeObject元素:
欢迎您提出任何其他想法……我想您可能会询问[std::list
由于许多间接因素,速度会非常慢]
std::vector<Foo*> objects
真正找到答案的唯一方法是运行性能测试。另外,您可能会对容器是否拥有指针感兴趣。容器中会有多少指针?它们是一次插入还是在一段时间内插入?是否要从容器中删除一些指针?列表
对于排序很重要并且在随机位置添加/删除元素非常有用。尤其是元素的范围。@Dialogicus:事实上,list
之所以真正引人注目,是因为它是一个基于节点的容器,这意味着它的迭代器失效条件非常稀疏,与vector非常不同。也就是说,你可以将迭代器存储到列表的一个元素中,进行任意数量的插入/删除,只要你没有删除你存储的迭代器指向的元素,它仍然有效。我想他对list
与vector
@Inverse感兴趣:我不知道如何理解这个“如果存储指针,std::list比std::vector好吗?”无论是list
还是vector
都会避免复制构造。这就是我想问的问题,否则对于3k的人来说,这是一个相当基本的问题。
element* items[]; /* array of pointers to items */
vector<SomeObject*> vecSomeObjectPointers;
vector<size_t> vecInvalidPointerIndices;// could be zero size, if no objects are removed
void AddNewObject()
{
SomeObject* ptrSomeObject = new SomeObject;
ptrSomeObject->PopulateSomeObject(); // add any extra memory if needed by SomeObject members
ptrSomeObject->DoAnyOtherWorkOnSomeObjectBeforeStoring();
size_t TotalInValidIndices = vecInvalidPointerIndices.size();
if(TotalInValidIndices > 0)
{
//re-use the invalid location/index of vector to hold new object
vecSomeObjectPointers[vecInvalidPointerIndices[TotalInValidIndices-1]] = ptrSomeObject;
vecInvalidPointerIndices.pop_back();
}
else vecSomeObjectPointers.push_back(ptrSomeObject); // new Object pointer at the end
}
void DeleteAnExistingObject()
{
//Find the index of the object to be deleted based on certain input criteria. Ex: value stored etc..
SomeObject* ptrSomeObject;
for(auto& itr=vecSomeObjectPointers.begin(); itr != vecSomeObjectPointers.end(); ++itr)
{
ptrSomeObject = itr;
if(ptrSomeObject->SomeObjectDeletionCriteriaSatisfied())
{
// make sure to delete any extra memory allocated to SomeObject members
delete ptrSomeObject; // re-claim allocated memory
// just invalidate the pointer info rather deleting it from vector to save re-shuffling time
itr = NULL;
//Store the corresponding index to be used for holding a new object to be inserted next time
vecInvalidPointerIndices.push_back(itr - vecSomeObjectPointers.begin());
}
}
}
void DoSomeWorkOnAnExistingObject()
{
SomeObject* ptrSomeObject;
for(auto& itr=vecSomeObjectPointers.begin(); itr != vecSomeObjectPointers.end(); ++itr)
{
if(itr == NULL) continue;
//Do some work on object data to maintain it
ptrSomeObject = itr;
}
}
std::vector<Foo*> objects
std::list<Foo> objects
class Foo
{
public:
~Foo(); /**<Now this must be recursive, otherwise it would need std::stack, which results in potential exception from dtor!*/
private:
std::vector<Foo*> m_children;
};