将对象指针保存在 STL 容器中是否比将对象本身保存更好

Is it better to save object pointer in STL container rather than object itself?

本文关键字:保存 对象 是否 更好 指针 存在 STL      更新时间:2023-10-16

假设我有一个类A,我需要一vectorA的对象。使用std::vector<A*>好还是std::vector<A>好?我遇到一些讲座提到前者不需要复制构造函数和复制赋值运算符的定义;而后者需要两者的定义。这是对的吗?

讲义并不完全正确:使用 A 向量使用复制构造函数/复制赋值运算符,但如果编译器提供的默认实现适合您,则无需提供自己的定义。

定义复制构造函数、赋值运算符和析构函数的决定与将对象放置在容器中的决策无关。当您的类手动分配其资源时,可以定义这三个。否则,默认实现应该有效。

回到主要问题,存储

指针与对象的决策主要取决于集合的语义,以及存储具有多态行为的对象的需求。

如果不需要多态行为,并且创建对象的副本相对便宜,则使用 vector<A> 是更好的选择,因为它允许容器为您管理资源。

如果复制成本高昂,并且需要多态行为,则需要使用指针。不过,它们不一定是原始指针:C++标准库提供了智能指针,可以为您处理清理,因此您不必手动销毁对象。

与许多其他现代语言不同,C++在值类型上蓬勃发展。

如果存储指针,则必须管理生成的对象生存期。 除非您使用unique_ptr或类似的智能指针,否则该语言不会帮助您。 失去对对象的跟踪是泄漏:在处理它们后跟踪它们是一个悬而未决的参考/指针。 两者都是非常常见的错误。

如果您存储值(或类似值的类型),并且教数据如何有效地移动自身,则vector会将其连续存储在内存中。

在现代计算机上,CPU 速度很快,内存速度慢得惊人。 一台典型的计算机将有 3 级缓存,以尝试使内存更快,但是如果您的数据分散在整个内存中(因为您使用免费存储来存储对象),CPU 几乎没有机会弄清楚您接下来要访问的位置。

如果你的数据位于一个连续的缓冲区中,不仅从内存中获取一个会得到多个对象,而且 CPU 将能够猜测你将需要在缓冲区中获取下一个内存块,并为你预取它。

因此,简短的版本是,如果您的对象大小适中,请使用对象的实际副本的向量。 如果它们稍微大一点,请将经常访问的内容放在对象中,将不常访问的大部分放在对象内的vector中,并编写有效的move语义。 然后将对象本身存储在vector中。

这有一些例外。

首先,值类型的多态性很难,因此您最终会大量使用免费商店。

其次,一些对象最终将其位置作为其身份的一部分。 矢量四处移动对象,"重新定位"对象的成本可能不值得费心。

第三,性能往往并不重要。 所以你做容易的事情。 同时,值类型并不难,虽然过早优化是一个坏主意,但过早去优化也是一个坏主意。 学习如何使用值类型和连续向量非常重要。

最后,学习零法则。 零规则是管理资源的对象应仔细编写其复制/移动/赋值/移动赋值/析构函数,以遵循值语义规则(或阻止)。 然后,使用这些资源管理对象的对象通常不需要实际编写其移动/复制/分配/移动分配/析构函数 - 它们可以留空、=default ed 或类似内容。

而且你不写的代码

往往比你写的代码有更少的错误。

这是正确的,这取决于。

在容器中存储指针为您提供了额外的灵活性,因为您不需要这些运算符,或者您可以使用这些带有副作用和/或高成本的运算符。在更糟糕的情况下,容器本身将执行指针的副本,其成本非常低。此外,您可以存储不同大小的对象(想到同一继承层次结构中不同类的实例,特别是如果它们具有虚拟方法)。

另一方面,存储对象本身可以降低访问开销,因为您不需要在每次访问元素时取消引用指针。此外,它将改善数据局部性(从而减少缓存未命中,页面未命中......)并减少内存消耗和碎片。

那里没有一般的经验法则。在其构造函数和复制运算符中没有副作用的小对象可能最好直接放在经常读取且很少修改的容器中,而具有构造函数和复制运算符的大型对象具有昂贵的副作用可能更适合经常修改、调整大小的容器......

您必须考虑您的用例并权衡每种方法的优缺点。

您需要检查身份吗? 如果是然后使用 unique_ptr 来存储它们,因为这样您以后就不需要注意删除它们了。

是的,您需要复制操作,因为如果将 A 存储到向量中,则向量会复制对象。使用 A* 时,矢量仅复制地址(指针)