假设您有某个类的std :: list。您可以通过两种方式制作此列表: 1)
std::list<MyClass> myClassList;
MyClass myClass;
myClassList.push_front(myClass);
使用此方法,将对象传递给列表时将调用复制构造函数。如果该类有许多成员变量,并且您多次进行此调用,则可能会变得昂贵。
2)
std::list<MyClass*> myClassList;
MyClass* myClass = new MyClass();
myClassList.push_front(myClass);
此方法不会调用该类的复制构造函数。我不完全肯定在这种情况下会发生什么,但我认为该列表将创建一个新的MyClass *并分配参数的地址。实际上,如果你在堆栈而不是堆上创建myClass并让它超出范围,那么myClassList.front()是无效的,因此必须如此。
如果我对此有误,请与我发生矛盾,但我相信第二种方法对某些类来说效率更高。
答案 0 :(得分:3)
这里要考虑的重点比性能问题更微妙。
标准库容器在复制语义上工作,它们会创建您添加到容器中的元素的副本。
通常,除非您绝对需要,否则最好远离C ++中的动态内存分配。第一个选项更好,因为您不必担心内存分配和解除分配,容器将获取您添加到其中的对象的所有权,并为您进行管理。
在第二种情况下,容器不会获取您添加的元素的所有权,您必须自己管理它。如果必须,那么你应该将智能指针作为容器元素而不是原始指针。
关于性能,您将需要对系统上的代码示例进行概要分析,以确定性能差异是否足以选择一种方法而不是另一种方法。
答案 1 :(得分:3)
这始终是一个问题。
首先,它取决于您的编译器是否支持C ++ 11移动语义,因为这会极大地改变问题的各个方面。
对于那些陷入C ++ 03的人
有多种选择:
std::list<MyClass> list;
list.push_front(MyClass());
即使在语义上有副本,优化器也可能会删除大多数冗余/死存储。大多数优化器都要求默认构造函数和复制构造函数的定义可用。
boost::ptr_deque<MyClass> deque;
std::auto_ptr<MyClass> p(new MyClass());
deque.push_front(p);
如果您将ptr_vector
替换为push_front
,则可以使用 push_back
,否则会有点浪费。这避免了std::list<MyClass*>
的大部分内存开销,并且还具有自动处理内存的额外好处。
boost::stable_vector<MyClass> svec;
svec.push_back(MyClass());
// ~~~~
有一个副本(与列表一样)但保证不应在容器内进行进一步复制(与列表一样)。它还允许比列表更多的操作(例如,随机访问),代价是在大容器的中间插入较慢。
对于那些喜欢C ++ 11
的人std::list<MyClass> list;
list.push_front(MyClass());
不生成任何副本,而是发生移动操作。
也可以使用提供的新操作来构建对象:
std::list<MyClass> list;
list.emplace_front();
将直接在节点内创建新的MyClass
,无需复制,无移动。
最后,您可能希望在容器上进行更紧凑的表示或其他操作,在这种情况下:
std::vector<std::unique_ptr<MyClass>> vec;
vec.emplace_back(new MyClass());
为您提供随机访问和较低的内存开销。
答案 2 :(得分:2)
如果您真的关心性能但仍需要使用链接列表,请考虑使用boost::intrusive::list。使用std::list
的主要问题是你需要从堆中分配新的内存,这可能比大多数情况下的复制结构更昂贵。由于boost::intrusive::list
会为您分配分配,因此您可以将对象保留在std::vector
中并批量分配。这样,您还可以获得更好的缓存局部性,这是性能方面的另一个问题。或者,您可以使用带有std :: list的自定义分配器来执行相同操作。由于使用std::list
的自定义分配器可能与使用提升侵入列表一样混乱,因此我会使用boost,因为您可以获得许多其他有用的功能(例如将相同的对象保留在多个列表中,等)。
答案 3 :(得分:0)
第一种方法的问题 - MyClass
很大时性能低,无法在两个数据结构中拥有相同的对象(在两个列表中,每个列表都有不同的语义;在列表和树中;等等)。如果这些缺点不打扰你,请采用第一种方法。
第二种方法更有效,但可能更难管理。例如,如果MyClass
对象不再可访问,则需要正确销毁它们。在存在异常的情况下这可能是非平凡的(阅读C++ exception safety)。我建议你看看Boost Smart Pointers,它打算简化C ++指针管理。 C ++ 11具有这些内置功能,因此如果使用现代编译器,则不需要Boost。请阅读Wikipedia以获得简短介绍。