7

:概念的スマートポインタを持つオブジェクトをクラス属性としてコピーする方法は?私はこの読み<a href="http://www.boost.org/doc/libs/1_52_0/libs/smart_ptr/smart_ptr.htm" rel="nofollow noreferrer">boost library documentation</a>から

を、スマートポインタは、それがもはや が必要なときにオブジェクトが 、指さないので、オブジェクトの削除に責任を所有すると見られています。

私は非常に単純な問題があります。コピー可能で割り当て可能なクラスのポインタ属性にRAIIを使用したいと考えています。

コピー操作と割り当て操作は深くなければなりません。すべてのオブジェクトには、実際のデータのコピーが必要です。また、RTTIは属性に対して使用可能である必要があります(そのタイプは実行時にも決定される可能性があります)。

コピー可能なスマートポインタ(データは小さいので、Copy on Writeポインタは必要ありません)の実装を検索するか、コピー操作をthis answerのようにオブジェクトのコピーコンストラクタに委譲しますか?

コピー可能で割り当て可能なクラスの単純なRAIIに対して、どのスマートポインタを選択しますか? 、

はここで生のポインタを使用して問題の擬似コードだ(私はクラスのコピーコンストラクタと代入演算子への委任のコピー/割り当て操作とunique_ptrを、適切な選択をするだろうと考えていますが、私はわかりません)私はすぐに使用を実現について聞いたことがありませんが、あなたは単に自分でそれを行うことができます

// Operation interface 
class ModelOperation 
{ 
    public: 
     virtual void operate =(); 
}; 

// Implementation of an operation called Special 
class SpecialModelOperation 
: 
    public ModelOperation 
{ 
    private: 
     // Private attributes are present here in a real implementation. 

    public: 

     // Implement operation 
     void operate() {}; 
}; 

// All operations conform to ModelOperation interface 
// These are possible operation names: 
// class MoreSpecialOperation; 
// class DifferentOperation; 

// Concrete model with different operations 
class MyModel 
{ 
    private: 
     ModelOperation* firstOperation_; 
     ModelOperation* secondOperation_; 

    public: 

     MyModel() 
      : 
       firstOperation_(0), 
       secondOperation_(0) 
     { 
      // Forgetting about run-time type definition from input files here. 
      firstOperation_ = new MoreSpecialOperation(); 
      secondOperation_ = new DifferentOperation(); 
     } 

     void operate() 
     { 
      firstOperation_->operate(); 
      secondOperation_->operate(); 
     } 

     ~MyModel() 
     { 
      delete firstOperation_; 
      firstOperation_ = 0; 

      delete secondOperation_; 
      secondOperation_ = 0; 
     } 
}; 

int main() 
{ 

    MyModel modelOne; 

    // Some internal scope 
    { 
     // I want modelTwo to have its own set of copied, not referenced 
     // operations, and at the same time I need RAII to for the operations, 
     // deleting them automatically as soon as it goes out of scope. 
     // This saves me from writing destructors for different concrete models. 
     MyModel modelTwo (modelOne); 
    } 


    return 0; 
} 
+0

[shared_ptrの説明](http://www.boost.org/doc/libs/1_52_0/libs/smart_ptr/shared_ptr.htm)を読むと、それらがCopyConstructibleとAssignableであることがわかります。 –

+1

@Joachimはい、同じオブジェクトへの参照を共有します。ディープコピーを実行するにはコピー操作が必要です。 – tmaric

+0

オプションでスマートポインタを使用していませんか? –

答えて

5

あなたのタイプのいくつかの要件を受け入れた場合、これはすべての種類の仮想クローン機能を実装する必要なしに行うことができます。特定の要件は、型にアクセス可能なコピーコンストラクタがあることです。コピーコンストラクタは、偶発的にスライシングする可能性があるため望ましくないと考えられます。しかし、friendingの適切な使用は、その欠点を緩和するかもしれません。

template <typename Base> 
struct clonable { 
    // virtual copy 
    // this clone function will be generated via templates 
    // no boilerplate is involved 
    virtual std::unique_ptr<clonable<Base>> clone() const = 0; 

    // expose the actual data 
    virtual Base* get() = 0; 
    virtual Base const* get() const = 0; 

    // virtual destructor 
    // note that this also obviates the need for a virtual destructor on Base 
    // I would probably still make it virtual, though, just in case 
    virtual ~clonable() = default; 
}; 

このインタフェースは、ほとんどの派生型でテンプレートクラスによって実装され、ひいてはが知っている:

、このような場合には、許容1は、コピー機能を提供するインタフェースの下で派生型を消去することにより、このついて行くことができていますコピーコンストラクタを通して通常のコピーを作成する方法。

template <typename Base, typename Derived> 
struct clonable_holder : clonable<Base> { 
    // I suppose other constructors could be provided 
    // like a forwarding one for emplacing, but I am going for minimal here 
    clonable_holder(Derived value) 
    : storage(std::move(value)) {} 

    // here we know the most derived type, so we can use the copy constructor 
    // without risk of slicing 
    std::unique_ptr<clonable<Base>> clone() const override { 
     return std::unique_ptr<clonable<Base>>(new clonable_holder(storage)); 
    } 

    Base* get() override { return &storage; } 
    Base const* get() const override { return &storage; } 

private: 
    Derived storage; 
}; 

これにより、追加の定型文なしで私たちの仮想コピー機能が生成されます。今、私たちはこれの上にスマートなポインタのようなクラスを作ることができます(それはポインタの意味を与えないのでスマートなポインタではなく、代わりに値の意味)。

template <typename Base> 
struct polymorphic_value { 
    // this constructor captures the most derived type and erases it 
    // this is a point where slicing may still occur 
    // so making it explicit may be desirable 
    // we could force constructions through a forwarding factory class for extra safety 
    template <typename Derived> 
    polymorphic_value(Derived value) 
    : handle(new clonable_holder<Base, Derived>(std::move(value))) { 
     static_assert(std::is_base_of<Base, Derived>::value, 
      "value must be derived from Base"); 
    } 

    // moving is free thanks to unique_ptr 
    polymorphic_value(polymorphic_value&&) = default; 
    polymorphic_value& operator=(polymorphic_value&&) = default; 

    // copying uses our virtual interface 
    polymorphic_value(polymorphic_value const& that) 
    : handle(that.handle->clone()) {} 
    polymorphic_value& operator=(polymorphic_value const& that) { 
     handle = that.handle->clone(); 
     return *this; 
    } 

    // other useful constructors involve upcasting and so on 

    // and then useful stuff for actually using the value 
    Base* operator->() { return handle.get(); } 
    Base const* operator->() const { return handle.get(); } 
    // ... 

private: 
    std::unique_ptr<clonable<Base>> handle; 
}; 

これはちょうど、最小限のインターフェイスですが、簡単に多くの利用シナリオをカバーするために、ここから肉付けすることができます。

+0

間違いなく詳細な回答のために1つ、ありがとう!しかし、これは、ポインタをラップし、それに対するコピーと割り当てを定義するよりはるかに複雑に思えます。これは私の経験の不足かもしれません...しかし、これは問題へのやや複雑なアプローチのようです... – tmaric

+0

"あなたは、ポインタをラップし、コピーと割り当てを定義する"アプローチでコピーを作成しますか? 'ModelOperation'インターフェースに' clone'関数を追加し、それを派生クラスごとに手動で実装しますか?このコードのすべてのコードは一度書き込まれ、派生クラスごとに一度ではなく、再利用されることに注意してください。 –

+0

また、あなたの例は例外ではないことに注意してください。 'secondOperation_ = new DifferentOperation();' throws?コンストラクタが完了しなければ 'MyModel'のデストラクタは呼び出されないので、前の行で作成された' MoreSpecialOperation'はちょうどリークします。スマートなポインタのようなクラスを使うと、自動的に安全になります。そしてこれはすべて定型文なしに:あなたの例は 'class MyModel {polymorphic_value firstOperation; polymorphic_value secondOperation;/*コピーしないctorsも、代入演算子もデストラクタも* /}書くことはありません; –

1

:それだけで問題の説明ではなく、実行されているC++コードです。

まず、仮想クローンメソッドを持つテンプレートラッパークラスを作成し、ストアドオブジェクトのコピーを返す必要があります。そして

コピー可能だろうし、忘れてはいけない、そのクラスの一部polymophicホルダーを書くにチェック削除 それはの新しいコピーを作成し、スマートポインタを作ることができるようにする必要性のように聞こえるhttp://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Checked_delete

1

別のスマートポインタオブジェクトが作成されるたびにオブジェクトを生成します。 (そのコピーが「深い」かどうかは、オブジェクトのコンストラクタによって決まります。格納しているオブジェクトは、私たちが知っているほど多くのレベルの所有権を持つ可能性があるので、「深い」は、私たちの目的のための主なものは、既存のオブジェクトへのポインタを取り出すのではなく、スマートポインタが別のオブジェクトからの参照で構築されているときに、別個のオブジェクトを作成するものが欲しいということです。私は問題を正しく理解しており、次に仮想クローンメソッドが必要になります。派生クラスのコンストラクタを正しく呼び出す方法は他にありません。

struct Clonable { 
    virtual ~Clonable() {} 
    virtual Clonable* clone() = 0; 
}; 
struct AutoPtrClonable { 
    AutoPtrClonable(Clonable* cl=0) : obj(cl) { } 
    AutoPtrClonable(const AutoPtrClonable& apc) : obj(apc.obj->clone()) { } 
    ~AutoPtrClonable() { delete obj; } 
    // operator->, operator*, etc 
    Clonable* obj; 
}; 

サンプルコードを使用するには、テンプレートにそれを作ること、など

+0

スマートポインタを完全に削除することを考えています。スマートポインタに関する所有権に基づくRAIIと、所有権の移転や参照の使用なしに、std ::またはboost ::で利用可能なコピー可能なスマートポインタが存在しないという事実の間には矛盾があるようです。 – tmaric

+1

@tomislav特殊なインターフェース(Clonableなど)を持たない限り、所有権の移転や参照の使用が必要です。ポインタによるオブジェクトの場合、コピーコンストラクタなどの通常の呼び出しはオブジェクトを複製できません。コンパイラはどのオブジェクトのコンストラクタを呼び出すのですか?最も派生したオブジェクトですか?これは仮想ルックアップを必要とし、C++では、それを得るために独自のclone()を書く必要があります。 –

+1

タイプにいくつかの要件がある場合は、クローン関数を使用せずにこれを行うことができ、単純に通常のコピーコンストラクターを操作することができます。このソリューションには、テンプレートを使用して外部からクローン機能を自動的に生成することが含まれます。 (私は空き時間が得られるとすぐに回答を書くことができますか?悲しいことに、私の現在のプロジェクトは本当に速くコンパイルされます) –

0

次の2つのソリューション(実際にはもっと多くを持っていますが、これらは:)私にはほとんど意味をなす)があります。

をまず、std::unique_ptrを使用できます。これは、ポインタごとに1つのインスタンスを持つ必要があるため、良い解決策です。(代わりにstd::shared_ptrを使用することもできますが、明示的にコードを追加しないと、shared_ptrのコピーと割り当てが「共有」されます(特に避けたいもの)。

あなたがstd::unique_ptrを使用している場合は、あなたのコピーコンストラクタと代入演算子は(unique_ptrをコンストラクタで指示先のインターフェイスで仮想clone方法、またはnew演算子のいずれかを使用して)明示的にディープコピーする必要があります。

第二に、あなた自身をロールバックすることができます。それについては何も複雑ではなく、小さな(10〜20行程度)ユーティリティクラスについて話しています。私は1つの場所で、このスマートポインタクラスを使用していた場合

個人的に、私はSTD :: unique_ptrをを使用します。そうでなければ(複数のポインタ、同じ振る舞い)私は自分自身をロールバックするので、(DRYの原則を維持するために)多くの場合に深いコピーを繰り返す必要はありません。

4

少し遅れますが、将来の視聴者のために:私のヘッダー専用ライブラリAuroraとそのSmartPtr tutorialにすぐに使用できる実装があります。オーロラでは、スマートポインタによるディープコピーを実装するのは簡単です。次のコードは、任意のコピー可能タイプTのために働く:

aurora::CopiedPtr<T> first(new T); 
aurora::CopiedPtr<T> second = first; // deep copy 

これは、あなたのクラスは、ポインタのメンバーを持っている場合、それは多くの場合、不要なビッグスリー/ファイブを実現することができます。

+3

それは決して遅すぎることはありません。私はあなたの答えを読んで、あなたの図書館が私が探していたものであることを確かめました。ありがとう! –

関連する問題

 関連する問題