新しい共有ポインタ&仮想関数

私のプログラムでは別の困難に直面しています(私はそれらをすべて保存し、多くの問題をバグで尋ねます:P)。私が持っているのは、ファンクタです。これらのファンクタはすべて仮想演算子()を持つ DuplicateFn から継承されており、各子はそのバージョンを書き込む予定です。

今では、マージと呼ばれる子の1つが成功しない可能性があります。その場合、フォールバックメソッドを試す必要があります。現在そのクラスは次のようになります。

class MergeFn : public DuplicateFn {
public:
    MergeFn() : FallBack(new SkipFn())
    {
    }
    MergeFn(GMProject const* Out, GMProject const* In, DuplicateFn* f) 
        : DuplicateFn(Out, In), FallBack(f)
    {
    }
    virtual void operator() (GMProject::pTree& tOut, const GMProject::pTree& tIn) const {
        if (!GMProject::isMergeable(tOut.GetName())) {
            (*FallBack)(tOut, tIn); //virtual table resolves this to the correct class
        } else {
        }
    }
private:
    std::shared_ptr FallBack;
};

私は問題がすでにここにはっきりと見えると思います - 非デフォルトのコンストラクタでは、このメソッドは指定されたパラメータの所有権を取得します。 - これは私が望むものではありません。それはそのパラメータをコピーし、そのパラメータの所有権を保持する必要があります。
今度は Fallback(new DuplicateFn(f))を試しましたが、純粋な仮想メソッドを使ってクラスからオブジェクトをインスタンス化しようとするコンパイルエラーが発生しました。

だから私はこれをどのようにすることができますか? - それぞれの型に対して特定のコンストラクタを指定する必要がありますか? - それをコアにコピーするだけですか?それともRTTIを通過しなければならないのですか?私はそこより良いアプローチがあることを願っています2。

edit To show how mergeFn gets initialized (and used):

std::unique_ptr foo;
foo.reset(new detail::MergeFn(this, &Other, DuplicateFns.at(HandleDuplicate)));

DuplicateFnsは、ユーザー入力(文字列)を関数ポインタに変換するのに役立つマップです。 - または今のように、DuplicateFn(ポインタ型はDuplicateFn *)からのサブタイプのオブジェクトへのポインタ

これは、コールバックメソッドとして使用されます

ProjectTree.combine_if(tree, &SimilarTreeValue, foo.get());

2つのツリーを1つにまとめる - SimilarTreeValueがtrueを返すとき、そのエントリーは重複していると見なされます。エントリがリーフの場合、3番目のパラメータが呼び出されます。つまり、私たちが話しているファンクタです。

0

3 答え

最も簡単な解決法(私の見解では)は、既に共有ポインタが必要なコンストラクタのシグネチャを変更することです:

MergeFn(GMProject const * Out,
        GMProject const * In,
        std::shared_ptr f) /* ... */

2番目のオプションは、クラス階層全体に clone()関数を与えることです:

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

struct Der1 : Base
{
  virtual Der1 * clone() const { return new Der1(*this); }
};

Then you can initialize FallBack(f->clone()).

個人的に私は最初のバージョンに行きたいと思います。また、共有ポインターを一意のポインターで置き換えることが現実的でないかどうかをチェックします。

1
追加された
@ KerrekSBうーん、しかし、私は本当に所有権を "移譲する"必要がある - 少なくとも私は外の世界がポインタに触れることができないようにする必要がある。しかし、同時に、このクラスによって外界を変更すべきではありません。
追加された 著者 paul23,
@curiousguy: unique_ptr は軽量です(動的割り当てなし、仮想ディスパッチなし)。所有権を共有する必要がある場合は、 shared_ptr のみを使用してください。
追加された 著者 Kerrek SB,
どの発信者ですか?私たちはそれを知らない。私が見ることができるのは、コンストラクタ引数です。 1人の所有者が1人だけ必要であることは全く可能です。とにかく、私たちは文脈について何も知らないので、OPだけがそれを決定することができます。
追加された 著者 Kerrek SB,
@ paul23:一意のポインタですべてを行うことができます。文脈なしで話すことは不可能です - クラスをどのように使用するかについての小さな代表的な例を投稿できますか?
追加された 著者 Kerrek SB,
"共有ポインタを一意のポインタで置き換える"なぜですか?
追加された 著者 curiousguy,
"所有権を本当に共有する必要がある場合はshared_ptrのみを使用してください"ここでは、呼び出し元と呼び出し先の少なくとも2人のオーナーが表示されています。タイプ unique_ptr の関数引数は、所有権の移転を意味します。呼び出し元が引き続き所有権を必要とする場合はどうなりますか?
追加された 著者 curiousguy,

あなたはカスタムデストラクタをとり、それに対して空の関数を渡すshared_ptrのコンストラクタを使うことができます。例えば、

void dontDelete(DuplicateFn *pFn ) {
 //Do nothing!
  }

class MergeFn : public DuplicateFn { 
public: 
    MergeFn() : FallBack(new SkipFn()) 
    { 
    }

    MergeFn(GMProject const* Out, GMProject const* In, DuplicateFn* f)  
        : DuplicateFn(Out, In), FallBack(f, dontDelete) 
    { 
    }

    virtual void operator() (GMProject::pTree& tOut, const GMProject::pTree& tIn) const { 
        if (!GMProject::isMergeable(tOut.GetName())) { 
            (*FallBack)(tOut, tIn); //virtual table resolves this to the correct class 
        } else { 
        } 
    }

private: 
    std::shared_ptr FallBack; 
}; 
1
追加された
"カスタムデストラクタを使用して空の関数を渡すshared_ptrのコンストラクタを使用できます。"これを読む度に "私は"安いトリックでデザイン問題から抜け出そうとしていますそれはしない "。これはもう一つの例です。
追加された 著者 curiousguy,

new DuplicateFn(f)

ファンクタ f だけをスライスします。

クローン機能が必要です。

#include 
#include 

template 
// runtime checked clone function
// T::do_clone() must be accessible to checked_clone
T *checked_clone (const T* that) {
    T *p = that->do_clone();
    assert (typeid (*p) == typeid (*that));
    return p;
}

// clone for an abstract class
#define IMPLEMENT_CLONE_ABSTRACT(Class) \
    friend Class *checked_clone (const Class* that); \
 \
public: \
    Class *clone_naked() const { \
        return checked_clone (this); \
    } \
    unique_ptr clone_unique() const { \
        return unique_ptr (checked_clone (this)); \
    } \     \
private: \
    virtual Class *do_clone() const = 0; \
/* end of IMPLEMENT_CLONE_ABSTRACT */

class Base {
    IMPLEMENT_CLONE_ABSTRACT(Base)
};

// clone for a concrete class
#define IMPLEMENT_CLONE_CONCRETE(Class) \
    friend Class *checked_clone (const Class* that); \
 \
public: \
    Class *clone_naked() const { \
        return checked_clone (this); \
    } \
    unique_ptr clone_unique() const { \
        return unique_ptr (checked_clone (this)); \
    } \     \
 \
private: \
    virtual Class *do_clone() const {  \
        return new Class (*this); \
    } \
/* end of IMPLEMENT_CLONE_CONCRETE */

class Derived : public Base {
    IMPLEMENT_CLONE_CONCRETE(Derived)
};
0
追加された