【问题标题】:Should I store entire objects, or pointers to objects in containers?我应该存储整个对象还是指向容器中对象的指针?
【发布时间】:2010-09-13 13:40:27
【问题描述】:

从头开始设计一个新系统。我将使用 STL 来存储某些长寿命对象的列表和地图。

问题:我是否应该确保我的对象具有复制构造函数并将对象的副本存储在我的 STL 容器中,或者我自己管理生命周期和范围并将指向这些对象的指针存储在我的 STL 容器中通常更好?

我意识到这在细节上有些不足,但我正在寻找“理论上的”更好的答案(如果存在),因为我知道这两种解决方案都是可能的。

玩指针有两个非常明显的缺点: 1) 我必须自己在 STL 之外的范围内管理这些对象的分配/解除分配。 2) 我无法在堆栈上创建临时对象并将其添加到我的容器中。

我还有什么遗漏的吗?

【问题讨论】:

  • 天哪,我喜欢这个网站,这是我今天想到的确切问题...感谢您为我提出的问题 :-)
  • 另一个有趣的事情是我们应该检查指针是否真的被添加到集合中,如果没有,我们可能应该调用 delete 以避免内存泄漏...... if ((set.insert(指针)).second = false) {删除指针;}

标签: c++ stl pointers


【解决方案1】:

因为人们正在关注使用指针的效率。

如果您正在考虑使用 std::vector 并且更新很少并且您经常迭代您的集合并且它是一种非多态类型存储对象“副本”将更有效,因为您将获得更好的参考位置.

Otoh,如果更新是常见的,存储指针将节省复制/重定位成本。

【讨论】:

【解决方案2】:

这真的取决于你的情况。

如果您的对象很小,并且复制对象是轻量级的,那么在我看来,将数据存储在 stl 容器中很简单且更易于管理,因为您不必担心生命周期管理。

如果你的对象很大,并且使用默认构造函数没有意义,或者对象的副本很昂贵,那么使用指针存储可能是要走的路。

如果您决定使用指向对象的指针,请查看Boost Pointer Container Library。这个 boost 库封装了所有 STL 容器,以便与动态分配的对象一起使用。

每个指针容器(例如 ptr_vector)在添加到容器时都会获得对象的所有权,并为您管理这些对象的生命周期。您还可以通过引用访问 ptr_ 容器中的所有元素。这可以让你做类似的事情

class BigExpensive { ... }

// create a pointer vector
ptr_vector<BigExpensive> bigVector;
bigVector.push_back( new BigExpensive( "Lexus", 57700 ) );
bigVector.push_back( new BigExpensive( "House", 15000000 );

// get a reference to the first element
MyClass& expensiveItem = bigList[0];
expensiveItem.sell();

这些类封装了 STL 容器并与所有 STL 算法一起工作,非常方便。

还有一些工具可以将容器中指针的所有权转移给调用者(通过大多数容器中的释放函数)。

【讨论】:

    【解决方案3】:

    如果您要存储多态对象,您总是需要使用基类指针的集合。

    也就是说,如果您打算在集合中存储不同的派生类型,则必须存储指针,否则会被切片守护进程吃掉。

    【讨论】:

    • 我喜欢切片守护进程!
    【解决方案4】:

    很抱歉在事件发生 3 年后才加入,但这里要注意...

    在我的上一个大型项目中,我的中心数据结构是一组相当简单的对象。项目开始大约一年后,随着需求的发展,我意识到对象实际上需要是多态的。花了几个星期艰难而讨厌的脑部手术来修复数据结构,使其成为一组基类指针,并处理对象存储、铸造等方面的所有附带损害。我花了几个月的时间让自己相信新代码是有效的。顺便说一句,这让我认真思考 C++ 的对象模型设计得有多好。

    在我当前的大项目中,我的中心数据结构是一组相当简单的对象。项目进行了大约一年(恰好是今天),我意识到对象实际上需要是多态的。回到网上,找到了这个线程,找到了 Nick 的 Boost 指针容器库的链接。这正是我上次为了解决所有问题而必须写的,所以这次我会试一试。

    无论如何,对我来说,道德:如果你的规范不是 100% 确定的,那就去寻求指导,你以后可能会为自己节省很多工作。

    【讨论】:

    • 规格从来都不是一成不变的。我不认为这意味着您应该专门使用使用指针容器,尽管 Boost 指针容器似乎使该选项更具吸引力。如果您决定将对象容器转换为指针容器,我怀疑您是否需要一次彻底检查整个程序。在某些设计下可能会出现这种情况。在这种情况下,这是一个脆弱的设计。在这种情况下,不要将您的问题归咎于对象容器的“弱点”。
    • 您可以将带有值语义的项留在向量中,并在其中进行多态行为。
    【解决方案5】:

    为什么不两全其美:做一个智能指针容器(例如boost::shared_ptrstd::shared_ptr)。您不必管理内存,也不必处理大型复制操作。

    【讨论】:

    • 这种方法与 Nick Haddad 建议的使用 Boost Pointer Container Library 有何不同?
    • @ThorstenSchöning std::shared_ptr 不添加对 boost 的依赖。
    • 你不能使用共享指针来处理你的多态性,所以你最终会用这种方法错过这个特性,除非你明确地转换指针
    【解决方案6】:

    通常将对象直接存储在 STL 容器中是最好的,因为它最简单、最有效,并且最容易使用对象。

    如果您的对象本身具有不可复制的语法或者是抽象基类型,您将需要存储指针(最简单的是使用 shared_ptr)

    【讨论】:

    • 如果你的对象很大,而且你经常移动元素,这不是最有效的。
    【解决方案7】:

    您似乎很清楚其中的区别。如果对象很小且易于复制,那么一定要存储它们。

    如果不是,我会考虑将智能指针(不是 auto_ptr,一个 ref 计数智能指针)存储到您在堆上分配的指针。显然,如果您选择智能指针,那么您将无法存储临时堆栈分配的对象(如您所说)。

    @Torbjörn 提出了一个关于切片的好观点。

    【讨论】:

    • 哦,并且 never ever ever 创建 auto_ptr 的集合
    • 对,auto_ptr 不是智能指针——它不引用计数。
    • auto_ptr 也没有非破坏性复制语义。将 auto_ptr 从 one 分配到 another 的行为将释放来自 one 的引用并更改 one
    【解决方案8】:

    使用指针会更有效,因为容器只会复制指针而不是完整的对象。

    这里有一些关于 STL 容器和智能指针的有用信息:

    Why is it wrong to use std::auto_ptr<> with standard containers?

    【讨论】:

      【解决方案9】:

      如果要在代码中的其他地方引用对象,请存储在 boost::shared_ptr 的向量中。这样可以确保在调整向量大小时指向对象的指针仍然有效。

      即:

      std::vector<boost::shared_ptr<protocol> > protocols;
      ...
      connection c(protocols[0].get()); // pointer to protocol stays valid even if resized
      

      如果没有其他人存储指向对象的指针,或者列表没有增长和缩小,则只需存储为普通旧对象:

      std::vector<protocol> protocols;
      connection c(protocols[0]); // value-semantics, takes a copy of the protocol
      

      【讨论】:

        【解决方案10】:

        这个问题困扰了我一段时间。

        我倾向于存储指针,但我有一些可能不适用于您的额外要求(SWIG lua 包装器)。

        这篇文章中最重要的一点是自己测试,使用你的对象

        我今天这样做是为了测试在 1000 万个对象的集合上调用成员函数 500 次的速度。

        函数根据 xdir 和 ydir(所有浮点成员变量)更新 x 和 y。

        我使用 std::list 来保存这两种类型的对象,我发现将对象存储在列表中比使用指针稍微快一些。另一方面,性能非常接近,因此取决于它们在您的应用程序中的使用方式。

        作为参考,在我的硬件上使用 -O3 时,指针需要 41 秒才能完成,而原始对象需要 30 秒才能完成。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-09-08
          • 2020-12-06
          • 1970-01-01
          • 1970-01-01
          • 2020-02-26
          • 1970-01-01
          • 2012-01-09
          • 2021-05-20
          相关资源
          最近更新 更多