C ++:深层复制指针(高效)

时间:2010-08-23 09:54:04

标签: c++ pointers deep-copy

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

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))方式而不是抱怨,然后看看它是否需要更快。但是,由于我最近花了一些时间来学习我的模式,我也认为我真的需要在某个时候重构这个项目。哦,我可能只是花了一些时间来处理这个问题,并掌握了我所掌握的所有新知识。因为没有一个答案说“只是坚持使用hashmap,稍后再看它是否真的需要更快”(如果有的话我真的很失望,因为它不是我的问题的答案),我是推迟回答答案,直到我回到这个项目。


编辑nr 3:我仍然没有回到这个项目。更准确地说,它已被无限期搁置。我很确定我现在不会过度弯腰O(m * log(n)),然后如果事后证明这是个问题,那么可能会稍后看看。然而,这对我的问题来说不是一个好的答案,因为我明确要求提高性能。我不想再接受答案了,我选择了最有帮助的答案并接受了答案。

3 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:0)

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

vector<int> m_indexes;

...

Card* ptr = &m_cards[m_indexes[0]];

可以无需更改即可复制带索引的矢量。

答案 2 :(得分:0)

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

首先(尽管与问题无关),我建议要么使用智能指针,要么使用普通结构。就您而言,这意味着使用vector<weak_ptr<Card> > m_ptrs代替原始指针更为有意义。

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

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

另一个非常重要的时刻是,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甚至在不复制对象的情况下也很容易损坏。