Глубокое копирование производного класса, когда его базовый класс имеет указатели на члены - PullRequest
0 голосов
/ 26 марта 2019

Я пытаюсь сделать глубокую копию объекта d класса Derived, как показано в коде ниже:

class A {
public:
   int m_int;
   A* clone() {
      return new A(*this);
   }
};

class Base {
public:
   A* m_a;
   virtual Base* clone() {
      A* new_a = new A();
      new_a = this->m_a->clone();
      Base* clone = new Base(*this);
      clone->m_a = new_a;
      return clone;
   }
};

class Derived : public Base {
public:
   double m_dbl;
   virtual Derived* clone() {
      return new Derived(*this);
   }
};

int main() {
   Derived* d = new Derived();
   d->m_dbl = 1.234;

   A* a = new A();
   a->m_int = -1;

   d->m_a = a;

   //clone d
   Derived d_copy = d->clone();

   //changing values of d's attributes must not affect d_copy
   a->m_int = 10;
   d->m_dbl = 3.456;

   //check d_copy
   cout << "m_int " << d_copy->m_a->m_int << endl;
   cout << "m_dbl " << d_copy->m_dbl << endl;
}

выход:

m_int 10 //wrong, should be -1;
m_dbl 1.234 //correct

Как видите, просто возвращать new Derived(*this) в методе Derived clone() неверно, поскольку он не копирует глубоко m_a.

Если я "сбью" глубокое копирование m_a с Base до Derived, тогда я получу правильный ответ:

   virtual Base* clone() = 0;

   ...

   virtual Derived* clone() {
      A* new_a = new A();
      new_a = this->m_a->clone();
      Derived* clone = new Derived(*this);
      clone->m_a = new_a;    
      return new Derived(*this);
   }
   //gives out m_int = -1

Если это так, значит ли это, что каждый раз, когда я создаю дополнительные производные классы из Derived, мне всегда приходится «сбрасывать» содержимое clone() на них?

Кроме того, если, например, Base имеет два производных класса Derived1 и Derived2, означает ли это, что я должен глубоко копировать элементы Base в каждом из них?

Есть ли другие способы подойти к этому?

1 Ответ

0 голосов
/ 26 марта 2019

Вы можете рассмотреть реализацию конструктора копирования, который всегда выполняет глубокое копирование.В этом случае ваша реализация clone всегда будет тривиальной:

class Base {
public:
   A* m_a;
   Base(const A& other)
       : m_a(other.m_a->clone())
   {       
   }

   virtual Base* clone() {
      return new A(*this);
   }
};

class Derived : public Base {
public:
   double m_dbl;
   Derived(const Derived& other)
       : m_dbl(other.m_dbl)

   virtual Derived* clone() {
      return new Derived(*this);
   }
};

Если вы не хотите изменять поведение конструктора по умолчанию (например, вы хотите, чтобы конструктор по умолчанию делал поверхностную копию)альтернатива - переместить реализацию копирования в метод CopyTo, чтобы вы могли использовать его повторно:

class Base {
public:
   A* m_a;
   static void CopyTo(const Base& from, Base& to)
   {
        to.m_a = from.m_a->clone();
   }

   virtual Base* clone() {
      Base* result = new Base();
      CopyTo(*this, result);

      return result;
   }
};

class Derived : public Base {
public:
   double m_dbl;

   static void CopyTo(const Base& from, Base& to)
   {
        Base::CopyTo(from, to);
        to.m_dbl= from.m_dbl;
   }

   virtual Derived * clone() {
      Derived * result = new Derived ();
      CopyTo(*this, result);

      return result;
   }

   virtual Derived* clone() {
      return new Derived(*this);
   }
};
...