如何实现具有循环引用的对象的深度复制或克隆

How to implement deep copy or clone of an object that has circular references?

本文关键字:深度 对象 复制 引用 循环 何实现 实现      更新时间:2023-10-16

我有这样的层次结构:

class Sphere;
class Cube;
class SpherePair;
class Entity {};
class Cube : public Entity {
public:
  list<Sphere*> spheres_;
};
class Sphere : public Entity {
public:
  Cube       *cube;
  SpherePair *spherepair;
};
class SpherePair : public Entity {
public:
  Sphere *first;
  Sphere *second;
};

我想要的是克隆Cube对象和所有连接到它的对象(Sphere、SphereAir、Cube)

立方体内部有球体,每个球体都是SphereAir对象的一半。SphereAir指向位于不同立方体或同一立方体中的球体。

这是正确的撤消功能所必需的。

我还想要一张旧实体和克隆实体的地图:

std::map<Entity*, Entity*> old_new;

添加:在这些循环引用之前,我有一个简单的克隆功能:

class Entity {
 public:
  virtual Entity* clone() = 0;
}

它被用于这样一个方案:

std::vector<Entity*> selected_objects_;
void move(const vec3f &offset) {
  document->beginUndo();
  for(int i = 0; i < selected_objects_.size(); ++i) {
    Entity *cloned = selected_objects_[i]->clone();
    cloned->move(offset);
    selected_objects_[i]->setDeleted(true);
    document->pushToUndo(selected_objects_[i]);
    document->addEntity(cloned);
  }
  document->endUndo();
}

我将发布整个代码块作为答案:

#include <iostream>
#include <list>
#include <map>
#include <assert.h>
using std::cout;
using std::endl;
using std::list;
using std::make_pair;
using std::map;
using std::pair;
class Cube;
class Sphere;
class SpherePair;
class Entity {
 public:
  virtual ~Entity() {}
  virtual Entity* clone() { return 0; }
  virtual Entity* cloneDeep(map<Entity*, Entity*> *old_new) { return 0; }
 protected:
  bool cloneAndPush(Entity *e, map<Entity*, Entity*> *old_new) {
    if (0 != old_new->count(e)) {
      return false;                             // already cloned
    }
    typedef pair<map<Entity*, Entity*>::iterator, bool> insert_result;
    Entity *cloned = e->clone();
    insert_result inserted = old_new->insert(std::make_pair(e, cloned));
    assert(inserted.second);
    return inserted.second;
  }
};
class Sphere : public Entity {
public:
  Sphere() {
    cout << "constructor Sphere" << endl;
  }
  virtual ~Sphere() {
    cout << "destructor Sphere" << endl;
  }
  virtual Entity* clone();
  virtual Entity* cloneDeep(map<Entity*, Entity*> *old_new);
  Cube       *cube;
  SpherePair *spherepair;
};
class Cube : public Entity {
 public:
  Cube() {
    cout << "constructor Cube" << endl;
  }
  virtual ~Cube() {
    cout << "destructor Cube" << endl;
  }
  virtual Entity* clone() {
    cout << "clone Cube" << endl;
    Cube *c = new Cube(*this);
    c->spheres_.clear();
    return c;
  }
  virtual Entity* cloneDeep(map<Entity*, Entity*> *old_new) {
    if (cloneAndPush(this, old_new)) {
      Cube *c = static_cast<Cube*>((*old_new)[this]);
      for(list<Sphere*>::iterator i = spheres_.begin(); i != spheres_.end(); ++i) {
        c->addSphere(static_cast<Sphere*>((*i)->cloneDeep(old_new)));
      }
    }
    return old_new->operator[](this);
  }
  void addSphere(Sphere *s) {
    spheres_.push_back(s);
  }
  void delSphere(Sphere *s) {
      spheres_.remove(s);
  }
  list<Sphere*> spheres_;
};
class SpherePair : public Entity {
 public:
  SpherePair() {
    cout << "constructor SpherePair" << endl;
  }
  virtual ~SpherePair() {
    cout << "destructor SpherePair" << endl;
    delete first;
    delete second;
  }
  virtual Entity* clone() {
    cout << "clone SpherePair" << endl;
    return new SpherePair(*this);
  }
  virtual Entity* cloneDeep(map<Entity*, Entity*> *old_new) {
    if (cloneAndPush(this, old_new)) {
      SpherePair *s = static_cast<SpherePair*>((*old_new)[this]);
      s->first = (Sphere*)first->cloneDeep(old_new);
      s->second = (Sphere*)second->cloneDeep(old_new);
    }
    return (*old_new)[this];
  }
  Sphere *first;
  Sphere *second;
};
Entity* Sphere::clone() {
  cout << "clone Sphere" << endl;
  return new Sphere(*this);
}
Entity* Sphere::cloneDeep(map<Entity*, Entity*> *old_new) {
  if (cloneAndPush(this, old_new)) {
    Sphere *s = static_cast<Sphere*>((*old_new)[this]);
    s->cube = (Cube*)cube->cloneDeep(old_new);
    s->spherepair = (SpherePair*)spherepair->cloneDeep(old_new);
  }
  return (*old_new)[this];
}
inline void populateListSimpleCase(list<Entity*> *ents) {
  Cube *first_cube = new Cube;
  Cube *second_cube = new Cube;
  // Cube *third_cube = new Cube;
  ents->push_back(first_cube);
  ents->push_back(second_cube);
  for (int i = 0; i < 3; ++i) {
    Sphere *first_cube_spheres = new Sphere;
    Sphere *second_cube_spheres = new Sphere;
    first_cube->addSphere(first_cube_spheres);
    first_cube_spheres->cube = first_cube;
    second_cube->addSphere(second_cube_spheres);
    second_cube_spheres->cube = second_cube;
    SpherePair *sp = new SpherePair;
    sp->first = first_cube_spheres;
    sp->second = second_cube_spheres;
    ents->push_back(sp);
    first_cube_spheres->spherepair = sp;
    second_cube_spheres->spherepair = sp;
  }
}
int main(int argc, char* argv[]) {
  list<Entity*> ent_list;
  populateListSimpleCase(&ent_list);
   map<Entity*, Entity*> old_new;
   (*ent_list.begin())->cloneDeep(&old_new);
  for (list<Entity*>::iterator i = ent_list.begin(); i != ent_list.end(); ++i){
    delete (*i);
  }
  ent_list.clear();
  return 0;
}

关于您的一般问题:将结构视为图,将内存中的对象视为顶点,将指针视为边。以深度优先的方式在结构中行走。通过浅层复制结构并逐个设置指针,保持旧结构中节点地址到新结构中节点的地址的映射(最初为空)。每次遇到地址已经被看到(在映射中)的子结构时,停止递归。

然而。。。

这是正确的撤消功能所必需的。

我不确定我是否理解你的观点,但你不需要循环引用来撤消。使用循环缓冲区(实际上只是一个带有一些标志和指针的平面数组),如果可能的话,使用结构共享来降低内存需求。

如果你的意思是你需要深度复制来撤消,那么我仍然建议你为你的对象考虑某种"平面"图形表示。指针结构适用于树和DAG,但用于一般图时非常令人困惑。

您可以将其视为序列化和反序列化的特殊情况。将要复制的对象序列化为字节流,然后将其反序列化为新对象。这个C++常见问题解答提供了一些关于如何序列化越来越复杂的对象图的好提示:http://www.parashift.com/c++-faq-lite/serialization.html\

当我需要做类似的事情时,我总是提到这里。

不确定这是否是您想要的,但这里要克隆一个图(有或没有循环引用)

#include <iostream>
#include <map>
using namespace std;
struct Thing{
 int length;
 int id;
 Thing *things[];
};
Thing* deepCopy(Thing *aThing,map<Thing*, Thing*> &aMap){
 Thing *newThing = new Thing();
 newThing->length = aThing->length;
 newThing->id = aThing->id;
 for(int i = 0 ;i < aThing->length;i++){
      auto it1 = aMap.find(aThing->things[i]);
      if(it1 == aMap.end()){
         aMap.insert(pair<Thing*,Thing*>(aThing,newThing));
         newThing->things[i] = deepCopy(aThing->things[i],aMap);
      }else{
         newThing->things[i] = it1->second;
      }
 }
 return newThing;    
}
int main(){
 Thing *aThing1 = new Thing();
 Thing *aThing2 = new Thing();
 Thing *aThing3 = new Thing();

 //////INITIALIZE GRAPH ///////// You can ignore this block
 aThing1->length = 2;
 aThing1->id = 1;
 aThing2->length = 2;
 aThing2->id = 2;
 aThing3->length = 1;
 aThing3->id = 3;
 aThing1->things[0] = aThing2; 
 aThing1->things[1] = aThing3; 
 aThing2->things[0] = aThing1; 
 aThing2->things[1] = aThing3; 
 aThing3->things[0] = aThing2; 
 //////END INITIALIZE GRAPH ///////// You can ignore this block
 map<Thing*,Thing*> aMap;
 Thing *myNewThing = deepCopy(aThing1,aMap);
 return 0;
}

我在一次采访中得到了这个,为了把它做好,我有点挣扎。我非常接近这个解决方案,但我使用了一个集合而不是地图,所以检索有点困难。

唯一重要的部分是deepCopy方法。剩下的只是样板代码。