2011-01-19 2 views
0

C++(Visual Studio 2010)で共有ポインタを使用し始め、次の問題が発生しています。クラスがスタックまたはデータメンバとしてインスタンス化されないようにする

私は、モジュールに特定の条件での動作方法を伝えるインターフェイスを定義する新しいモジュールを作成しています。これは、(ちょうど私の問題を説明するための人工例)このようなものです:

// Interface that should be implemented by user of the module 
class RingAlert 
    { 
    public: 
     virtual void ring() = 0; 
    }; 

// Module that does something important 
class Module 
    { 
    public: 
     Module (RingAlert &ringAlert) : m_ringAlert(ringAlert) {} 
     void dosomething(); // may call RingAlert::ring if something goes wrong. 
    private: 
     RingAlert &m_ringAlert; 
    }; 

はモジュールのユーザーのために、それを容易にする、とRingAlertは他のモジュールに渡される可能性があるため、私は今、これを作っていますこのような共有ポインタは、:

typedef std::shared_ptr<RingAlert> RingAlertPtr; 

class Module 
    { 
    public: 
     Module (RingAlertPtr ringAlert) : m_ringAlert(ringAlert) {} 
     void dosomething(); // may call RingAlert::ring if something goes wrong. 
    private: 
     RingAlertPtr m_ringAlert; 
    }; 

今モジュールのユーザーは、新しいRingAlertインスタンスを作成することができ、単にどこかでそれを維持し、アプリケーションの終了時に、それを削除せずに、モジュールに渡します。この例では

class MyRingAlert : public RingAlert 
    { 
    public: 
     virtual void ring() {std::cout << "ring ring" << std::endl;} 
    }; 

class Application 
    { 
    public: 
    private: 
     MyRingAlert m_myRingAlert; 
    }; 

// later, somewhere in application code 
Module m(RingAlertPtr(&m_myRingAlert)); 

アプリケーションは、データメンバのアドレスを取得し、共有ポインタにそれを置く、アプリケーションがこのような何かをしなければ

問題が開始されます。 アプリケーションの後で、モジュールのデストラクタは共有ポインタを削除し、参照カウントを減らします。を削除します。これはアプリケーションのデータメンバーであるため、削除しないでくださいクラス。

私は(ラムダのを使用しても機能を使用して、わずかにクリーナーソリューションを持っている)、これを実行してインスタンスを削除するから共有ポインタを防止する方法を見つけた:

Module m(RingAlertPtr(&m_myRingAlert,[](void *){}); 

これは私の問題を解決するが、私は」なぜなら、ApplicationクラスがModuleクラスの前で破壊されたとしても問題は残るからです。

唯一の良い解決策は、新しい RingAlertのインスタンス(または実際にRingAlertを実装するクラス)へのアプリケーションの残りの部分のようです。

これはできますか? スタック上の基本クラスのサブクラスまたはdatamemberとしてコードをインスタンス化できないようにする方法はありますか? 言い換えれば、インスタンス化したい場合、ベースクラスのサブクラスを強制的に新しくすることができますか?

+0

[私に派生したクラスのオブジェクトを "new"のみで作成する方法をユーザーに強制する方法](http://stackoverflow.com/questions/2833752/how-to-enforce-users-to-create -objects-of-class-derived-from-new-only) – sharptooth

答えて

3

これは根本的に欠陥があります。メインクラスに継承してそのコールバックを提供したいのですが?あなたは人工的にそれを実質的な利点のために制限しています。代わりにstd::functionを使用してください。

class Module { 
public: 
    Module (std::function<void()> ringAlert) : Alert(ringAlert) {} 
    void dosomething() { 
     if (something) alert(); 
    } 
private: 
    std::function<void()> alert; 
}; 

標準機能タイプは非常に柔軟性があり、使用するのに非常に適しています。

カスタム破壊関数を提供することもできます。

class Module { 
public: 
    Module(interface* ptr, std::function<void(interface*)> destructor) 
     :pointer(ptr), destruct(destructor) { 
    } 
    ~Module() { 
     if (destruct) 
      destruct(pointer); 
    } 
    void doSomething() { 
     if (condition) 
      pointer->ring(); 
    } 
private: 
    interface* pointer; 
    std::function<void(interface*)> destruct; 
}; 

私がスタックに割り当てる場合は、空の関数オブジェクトを渡します。私がヒープに割り当てる場合、私は迅速な破壊ラムダを渡します。そして、それはノーで正しい方法を意味しています - - 私がやる傾向にある何

+0

例を簡単にするために、インターフェイスに1つのメソッドしか追加しませんでした。実際には、インタフェースには複数のメソッドがあるので、関数ポインタ、ファンクタ、またはラムダを使うだけでは十分ではありません。 – Patrick

+0

@Patrick:どうしてですか?インタフェースから継承し、そのメンバであるすべてをその多くの関数オブジェクトに割り当てるよりも多くのコードがあります。 – Puppy

+0

いいアイデア。私はさらにそれを見ていきます。 – Patrick

0

何を返す私はいつもの解決策は、コンストラクタ(S)protectedを作るだけstatic(または無料friend機能)を介して作成できるようにと考えているクラスRingAlert

0

のプライベート型依存オーバーライド演算子newdeleteを作成するについてshared_ptr<>。コンストラクターは保護されているため、フレンドでないコードでは、インスタンスがスタック上に構築されようと、コンストラクター内でメンバーとして構成されようと、インスタンスを作成できません。

もちろん、子クラスでもコンストラクタを確実に保護する必要があります。

+0

それは私の主張です。どのようにすべての子クラスに対してもこれを強制できますか? – Patrick

+0

私はあなたがソースコードの問題としてできるとは思わない。しかし、あなたは行動を強制するためのリソースとして単体テストとドキュメンテーションを持っています。子クラスがうまく動作しない場合、コンパイルに失敗したテンプレート化ユニットテストを提供します。それを文書化する。 –

1

または、次のようにも最善の方法は次のとおりです。

struct IType 
{ 
    typedef shared_ptr<IType> Ptr; 

    virtual DoSomething() = 0; 

protected: 
    virtual ~IType() { } 
}; 

class Type : public IType 
{ 
public: 
    typedef shared_ptr<Type> Ptr; 

    static Ptr New() 
    { 
     return Ptr( 
        new Type(), 
        &Type::Delete 
       ); 
    } 

private: 

    virtual void DoSomething(); 

    Type(); // Defined in the CPP file. 

    static void Delete(Type* p) 
    { 
     delete (p); 
    } 
}; 

ですからしかType型のオブジェクトを作成することができます:: Ptrを、囲まれた型は、.get()メソッドで削除することはできません。

これが役に立ちます。

+0

あなたのクラスがむしろ「スタンドアロン」クラスである場合、これは機能します。私の場合、すべてのサブクラスのインスタンス化をnewを使用して強制的に作成したいと考えています。 – Patrick

+0

実際、私はこのモデルをクラスとインタフェースの階層に使用する傾向があります。そのようにその使用を妨げるものは何もありません。 – Nick

0

RingAlertのコンストラクタをプライベートにして、newを呼び出してポインタを返す静的メソッドを作成します(またはより良い方法ではshared_ptrを返します)。

関連する問題