9

我正在学习数据结构类,在教授的所有示例中,他总是让他的地图具有指向结构或容器的指针的值,而不是保存结构或容器本身。

他只是将其作为一种习惯,还是有诸如提高速度之类的充分理由?

  • 我知道您可以使用指向数据的指针来避免数据的冗余副本,但仍然可以同时在多个容器/结构中容纳指向该数据的方向。
  • 在这些示例中,情况并非如此。数据仅在该地图中。
4

3 回答 3

11

在我看来,决定是否使用指针与对象涉及许多因素:

1. 你是否需要多态性?

如果要维护基类对象的容器,然后在其中存储各种派生类的对象,则必须使用指针,否则将无法正确解析虚函数调用。

2.您存储的对象的大小及其对复制操作的适用性

指针可能优于对象的关键原因之一是对容器执行的各种操作都涉及复制存储在其中的对象。对于许多存储操作(例如std::vector<>::push_back()or std::map<>::insert())、一些检索操作(例如std::vector<>::operator[],然后将对象存储在局部变量中)以及由容器“内部”执行的一些操作(例如重新分配当向量超出其容量时,或重新散列std::unordered_map<>. 请注意,复制操作可能不太重要,具体取决于您如何选择容器以及如何使用它(例如,std::vector<>::reserve()用于分配足够的空间、std::vector<>::emplace_back()用于存储以及从不制作检索到的元素的本地副本可能意味着没有副本是做过)。

但是,如果您希望制作大量副本(或者如果分析现有代码显示制作了许多副本),那么使用指针而不是对象显然会有所帮助,因为指针很小并且在内存中对齐良好。再说一次,如果您存储的对象实际上小于指针,这将没有多大意义。

3.您对容器及其内容执行的其他操作

即使您正在处理的对象大于指针并且您期望大量的复制操作,使用指针也不一定是可取的。考虑一种情况,您存储大量中等大小的对象(例如,每个 16 字节),并且您经常需要遍历整个容器并执行某种统计计算。当您将这些对象直接存储在向量中时,您将在迭代期间获得出色的缓存效率:当您检索一个对象时,将从内存中检索整个缓存行,从而使接下来几个对象的检索速度更快。使用指针时通常不是这种情况;相反,在检索到一个元素后,指针必须被取消引用,从而导致另一个可能未缓存的内存区域的移动操作。

很明显,这完全取决于您存储的对象的类型和大小,以及您执行的操作的类型和频率。如果您正在处理的对象是 GUI 应用程序的各种类型的窗口、按钮和菜单,您很可能希望使用指针并利用多态性。另一方面,如果您正在处理紧凑元素的巨大结构,大小和形状都相同,并且您执行的操作涉及频繁的迭代或批量复制,那么直接存储对象是可取的。在某些情况下,如果不尝试两者并根据内存和时间基准的结果做出决定,就很难做出决定。


最后一点,如果您最终使用指针,请考虑您正在构建的容器是您在堆上分配的对象的最终所有者,还是只是维护临时指针。如果容器是这些对象的所有者,那么建议您使用智能指针而不是原始指针。

于 2012-09-17T01:56:56.320 回答
2

将对象实例直接存储在容器中的好处是可以避免一定程度的间接性,并且可以节省指针本身使用的空间。通过直接存储对象实例而不是存储指针,您可以在时间和空间效率方面取胜。如果您对处理器缓存内存的工作原理有所了解,就不难看出将对象实例“内联”存储在容器中可以带来真正的性能优势。

在不对包含的类型或容器使用模式做出任何假设的情况下,默认容器应该是std::vector<T>(而不是std::vector<T*>)。从该默认选择开始,如果您可以看到使用模式将如何从该其他类型结构的性能配置文件中受益,您将使用除向量之外的其他东西。同样,如果需要指针间接或在性能方面似乎值得,您将拥有指向对象的容器存储指针。如果包含的类型不是可复制构造的,则需要间接,如果容器不“拥有”其对象,则也需要间接。

于 2012-09-17T01:56:34.980 回答
1

一种可能性是内容类型不可复制。

于 2012-09-17T00:52:44.203 回答