web-dev-qa-db-ja.com

C ++:基本クラスポインタのディープコピー

私は周りを検索しましたが、これを実行するには、Baseクラスを変更する必要があり、これが最善のアプローチであるかどうかを知りたいようです。たとえば、私は基本クラスを持っています:

class Base {}

次に、派生クラスの長い行:

class Derived_1:: public Base {}
class Derived_2:: public Derived_1{}
...
...
class Derived_n:: public Derived_M{}

そして、私は別のクラスを持っています:

class DeepCopy 
{ 
  Base * basePtr;

  public:
   DeepCopy(DeepCopy & dc) {}
}

BaseクラスとDerived_xクラスのコピーコンストラクターが適切にコーディングされていると仮定すると、DeepCopyのコピーコンストラクターを作成するための最良の方法は何ですか。コピーしようとしているオブジェクトのbasePtrにあるクラスをどのように知ることができますか?

私が考えることができる唯一の方法はRTTIを使用することですが、dynamic_castsの長いリストを使用することは正しくないようです。さらに、DeepCopyはBaseクラスの継承階層について知る必要があります。

私が見た他の方法は ここ です。ただし、BaseクラスとDerivedクラスがcloneメソッドを実装する必要があります。

それで、これを行うためのはるかに簡単で標準的な方法はありますか?

23
madu

仮想コピーパターンを使用する必要があります。コピーを実行するインターフェイスに仮想関数を提供し、階層全体に実装します。

struct base {
   virtual ~base() {}                // Remember to provide a virtual destructor
   virtual base* clone() const = 0;
};
struct derived : base {
   virtual derived* clone() const {
      return new derived(*this);
   }
};

次に、DeepCopyオブジェクトはその関数を呼び出す必要があります。

class DeepCopy 
{ 
  Base * basePtr;    
public:
   DeepCopy(DeepCopy const & dc)           // This should be `const`
      : basePtr( dc.basePtr->clone() )
   {}
};

clone()関数を使用するアプローチを使用することは良い解決策です。 CRTP(不思議なことに繰り返されるテンプレートパターン) を使用すると、作業の一部を節約できます。これを行う方法は、テンプレートであり、clone()関数を実装する中間レベル(以下ではBaseCRTPと呼ばれます)を導入することです。実際のクラスを派生させるときは、それらを派生元のベースのテンプレート引数として使用します。 clone()関数が自動的に実装されます。派生クラスがコピーコンストラクターを実装していることを確認してください(またはデフォルトが必要なものであることを確認してください)。

/* Base class includes pure virtual clone function */
class Base {
public:
  virtual ~Base() {}
  virtual Base *clone() const = 0;
};

/* Intermediate class that implements CRTP. Use this
 * as a base class for any derived class that you want
 * to have a clone function.
 */
template <typename Derived>
class BaseCRTP : public Base {
public:
  virtual Base *clone() const {
      return new Derived(static_cast<Derived const&>(*this));
  }
};

/* Derive further classes. Each of them must
 * implement a correct copy constructor, because
 * that is used by the clone() function automatically.
 */
class Derived1 : public BaseCRTP<Derived1> {
  /*... should have an ordinary copy constructor... */
};

class Derived2 : public BaseCRTP<Derived2> {
  /*... should have an ordinary copy constructor... */
};

その後、通常の方法でDeepCopyクラスを実装できます。

class DeepCopy 
{ 
  Base *basePtr;    
public:
  DeepCopy(const DeepCopy &dc)
    : basePtr(dc.basePtr->clone())
  {}
};
23
jogojapan

この状況では、テンプレートが最善の方法だと思います。

template<typename Sub>
class DeepCopy
{
    Base *base;

    DeepCopy(Sub *sub)
    {
        base = new Sub(*sub); // use copy constructor
    }
}

これは、DeepCopyが相互に割り当てられないことを意味しますが、それはC++で支払う価格です。

1