【问题标题】:C++: Updating pointers in deep copy (efficiently)C++:在深拷贝中更新指针(有效)
【发布时间】:2010-08-23 09:54:04
【问题描述】:

我的问题最好用一个代码示例来说明,所以让我们开始吧:

class Game
{
    // All this vector does is establish ownership over the Card objects
    // It is initialized with data when Game is created and then is never
    // changed.
    vector<shared_ptr<Card> > m_cards;

    // And then we have a bunch of pointers to the Cards.
    // All these pointers point to Cards from m_cards.
    // These could have been weak_ptrs, but at the moment, they aren't
    vector<Card*> m_ptrs;

    // Note: In my application, m_ptrs isn't there, instead there are
    // pointers all over the place (in objects that are stored in member
    // variables of Game.
    // Also, in my application, each Card in m_cards will have a pointer
    // in m_ptrs (or as I said, really just somewhere), while sometimes
    // there is more than one pointer to a Card.
}

现在我想做的是制作这个 Game 类的深层副本。我创建了一个新向量,其中包含新的 shared_ptrs,它指向新的 Card 对象,它们是原始 Card 对象的副本。这部分很简单。

那么麻烦就来了,要更新m_ptrs的指针指向m_cards中的卡片,这可不是一件简单的事。

我能想到的唯一方法是创建一个映射并在复制 m_cards 期间填充它(使用map[oldPtr] = newPtr),然后使用它来更新 m_ptrs。但是,这只是O(m * log(n)) (m = m_ptrs.size(); n = m_cards.size())。由于这将是一个非常常规的操作* 我想有效地执行此操作,并且我觉得在 O(m) 中使用自定义指针应该是可能的。但是,我似乎无法找到一种有效的方法来做到这一点。有谁做过吗?

*它用于为 AI 创建一个测试平台,让它“尝试”不同的动作


编辑:我想补充一点关于接受答案的信息,因为我还没有。我一直在等到我回到这个项目(我在这个项目上工作了太多,所以我走上了旁道——如果你这样做是为了好玩,那就必须保持乐趣),所以在我接受之前可能需要一段时间答案。不过,我会在一段时间内接受答案,所以不要担心:P


编辑 nr 2:我还没有回到这个项目。现在,我正在考虑采取O(m * log(n)) 的方式而不是抱怨,然后再看看它是否需要更快。然而,由于我最近花了一些时间来学习我的模式,我也认为我真的需要一些时间来重构这个项目。哦,我可能会花一些时间利用我掌握的所有新知识来解决这个问题。由于没有答案说“只需坚持使用哈希图,稍后再看看它是否真的需要更快”(如果有的话,我实际上会非常失望,因为这不是我问题的答案),我是在我回到这个项目之前再推迟选择答案。


编辑 nr 3:我仍然没有回到这个项目。更准确地说,它已被无限期搁置。我很确定我现在不会把头放在O(m * log(n)) 上,如果结果证明是个问题,也许以后再看看。但是,这并不是我的问题的好答案,因为我明确要求更好的性能。不想再让答案不被接受,我选择了最有帮助的答案并接受了它。

【问题讨论】:

  • 我不确定我是否理解您为什么认为您需要 2 个具有相同数据的向量。对我来说,这听起来像是数据缺陷的重复。请解释您为什么需要/想要这个?
  • @John Dibling:请阅读。指针数组是使问题更容易的简化。实际上它并不存在——而是指针分布在 Game 拥有的多个类中。那是问题所在。此外,它(更详细地)在此页面的其他地方。
  • 在做类似的事情时,我使用了一个映射来将 oldptrs 映射到 newptrs,尽管在我的情况下这并不重要,因为该映射仅在复制时使用,因此没有必要进行优化。你认为你可以重构你的项目以帮助你的改变更容易引入吗?
  • @n1ck:问题是复制是人工智能的核心部分,在这样的应用程序中,这绝对是一个瓶颈。基本上,即使是打出单张牌的效果,AI 也不知道,所以它必须复制游戏才能找到它。那么,打出一连串的牌,也需要一份游戏副本才能看到效果。最后,要再次看到预期计划的可能结果,还需要复制游戏。基本上,复制游戏将是 AI 的核心。
  • @n1ck:当您希望我重构项目时,我不完全理解您的意思。但是,使用不同的指针类将最大限度地减少我必须对代码进行的更改次数。这是一个不错的奖励,尽管这并不是建议这种方法的真正理由——这是由对我来说似乎是正确的设计所引发的(因为该软件仍处于早期 alpha 阶段,我没有最后期限,并且因为我有点理想主义,我更喜欢适当的设计)。

标签: c++ pointers deep-copy


【解决方案1】:

将指针存储为索引。 正如您所说,它们都指向 m_Cards ,这是一个可以索引的向量(这是正确的英语吗?)。 要么您这样做只是为了存储并将它们转换回加载时的指针。 或者您可能会考虑通常使用索引而不是指针。

【讨论】:

  • kaptnole,我已经根据我的浏览器spillchucker 进行了编辑。哦,还有+1,因为我同意。
  • 所以基本上,我创建了一个指针来存储对向量和索引的引用。听起来不错——如果做得好,它可能可以用真正的指针语法来完成。我需要研究细节,但这听起来不错。
  • 我遇到了一些问题,因为除了使用许多 Card,我还使用了许多 SpecialCard(其中 SpecialCard 继承自 Card ) 这也指向 m_cards 的成员并且也应该更新。不过,老实说,这不是问题。
【解决方案2】:

如何保持卡片元素索引而不是指针:

矢量 m_indexes; ... 卡片* ptr = &m_cards[m_indexes[0]];

带有索引的向量可以被复制而无需更改。

【讨论】:

  • 我不喜欢这个解决方案,因为它需要我将 m_cards 的引用分发给几十个类,看到卡片指针并没有真正在同一个类中使用,但是到处都是。不过,它确实解决了问题。
  • @Jasper 为什么你必须提供对 m_cards 的引用?你还不能分发指向其他类的实际指针吗?
  • 因为示例被简化,指针实际上并不位于同一个类中——它们位于 Game 拥有的类中——所有这些指针都需要指向新的 Card 对象。因此,使用索引而不是指针,但在 Game 类以外的类中使用指针(除了其中一个:P 之外的所有类)并不能完全解决问题。
【解决方案3】:

我最近遇到了一个非常相似的问题:将指针和std::vector实现的类内部结构克隆为对象存储。

首先(尽管与问题无关),我建议要么坚持使用智能指针,要么使用简单的结构。在您的情况下,这意味着使用 vector&lt;weak_ptr&lt;Card&gt; &gt; m_ptrs 而不是原始指针更有意义。

关于问题本身 - 另一种可能的解决方法是在复制构造函数中使用指针差异。我将针对对象向量演示它,但使用共享指针将利用相同的原理,唯一的区别在于复制 m_cards(如果您想要对象克隆,则不应简单地使用赋值,而是复制 m_cards element-by -元素)。

非常重要,该方法仅适用于保证存储元素的容器(向量、数组)。

另一个非常重要的时刻是m_ptrs 元素应该只代表内部 Card 结构,即。 e.它们必须只指向内部的m_cards 元素。

// assume we store objects directly, not in shared pointers
// the only difference for shared pointers will be in
// m_cards assignment
// and using m_cards[...].get() instead of &m_cards[...]
vector<Card> m_cards;
vector<Card*> m_ptrs;

在这种情况下,您的指针数组可以通过使用线性时间的指针算法轻松计算。在这种情况下,您的复制构造函数将如下所示:

Game::Game(const Game &rhs) {
  if (rhs.m_cards.empty())
    return;

  m_cards = rhs.m_cards;

  // if we have vector of shared pointers
  // and we need vector of pointers to objects clones
  // something like this should be done
  // for (auto p: rhs.m_cards) {
  //   // we must be certain here that dereferencing is safe,
  //   // i. e. object must exist. If not, additional check is required.
  //   // copy constructor will be called here:
  //   m_cards.push_back(std::make_shared<Card>(*p));
  // }

  Card *first = &rhs.m_cards[0];
  for (auto p: rhs.m_ptrs) {
    m_ptrs.push_back(&m_cards[p - first]);
  }
}

基本上,在这种 deepcopy 方法中,您仍将使用索引,但您保留了在其他类方法中使用指针的便利性,而无需单独存储索引。

无论如何,对于使用这种结构,你应该确切地知道你对类成员做了什么以及为什么,这需要更多的手动控制(例如,至少在m_cards 中添加/删除元素应该是有意识地完成,在其他情况下 m_ptrs 即使不复制对象也很容易损坏)。

【讨论】:

    猜你喜欢
    • 2015-07-21
    • 1970-01-01
    • 2010-12-28
    • 1970-01-01
    • 1970-01-01
    • 2010-12-16
    • 2022-01-09
    • 1970-01-01
    • 2016-12-18
    相关资源
    最近更新 更多