2010-12-21 16 views
1

新しいC++アプリケーションの設計に忙しいです。このアプリケーションでは、ポインタによる潜在的なエラーを最小限に抑えたいので、アプリケーションは普通のC++(.Netや他の派手なもの)でなければならないので、共有ポインタを調べています。C++で共有ポインタを扱いやすくする方法

私はそれが簡単に共有ポインタ、例えばで動作するようにするためにいくつかのトリックを働いた:、のPtr ::あなたは簡単にXを書くことができます

class X 
    { 
    public: 
     ... 
     typedef std::shared_ptr<X> Ptr; 
    }; 

その方法:このように、クラス内のtypedefを使用してどこでも "std :: shared_ptr"よりも書く方が簡単です。

私はまた、共有のポインタにいくつかの欠点に気づい:どこでも私は、私はちょうどポインタを使用したい場合、私はもう宣言を前方に使用することはできません私は<memory>

  • を含める必要が共有ポインタを使用

    共有ポインタを使いやすくするための他の秘訣はありますか?

  • +2

    前に提案したようにtypedefを使用しましたが、 'shared_ptr 'を直接読みやすくするためにこれをやめました。代わりに、私はshared_ptrをグロ​​ーバルな名前空間に引っ張って書けるようにしたい( 'std :: shared_ptr;を使って)。個人的な味の話。 – ltjax

    答えて

    7

    DO NOT!

    ポインタエラーを最小限にする唯一の方法は、の右のポインタタイプを使用することです。そしてそれはタイプ、複数です。

    共有ポインタは銀色の弾丸ではありません。循環参照があるとすぐにメモリリークになります(どこでも)。

    エラーのないC++アプリケーションが必要な場合は、そのアプリケーションで作業する必要があります。あなたはあなたのアプリケーションを理解する必要があります。異なるオブジェクトの所有権セマンティクスを理解する必要があります。共有ポインタは共有された所有権を与えますが、これは一般にかなり低い分母です。他のすべては共有された所有権に置き換えられ、それはうまくいくでしょう。

    しかし、デフォルトのケースでは、オブジェクトがの1つの他のエンティティによって所有されていることがあります。これは関数が所有し、その関数が返ってきたときに破棄するか、クラスによって所有されているか、それ以外のものであれば破棄する必要があります。しばしばポインタは必要ありません。オブジェクトはstd::vectorの値で格納されます。それは単なるローカル変数またはクラスメンバーです。 のポインタの場合は、scoped_ptrまたは所有権の転送を許可するポインタ(unique_ptrまたはauto_ptr)でよく表現されます。

    shared_ptrは、オブジェクトの存続期間または所有権について保証することができない場合があります。しかし、それを使用するときは、サイクルを中断するためにweak_ptrも使用する必要があります。

    実際には、より良い方法はです。できるだけ多くのポインタをに避けることです。 の場合にポインタが必要な場合は、できるだけ具体的な所有権の意味を持つものを使用してください(scoped_ptr、所有権の譲渡を一切許可しない場合は、必要に応じてに移動してください)。所有権、などunique_ptr、そしてあなたがC++のコードを作るために振ることができます魔法の杖はありません唯一のあなたは、任意の数のクライアントの間で自由に所有権を共有することができますあなたがshared_ptrを使用する必要があります最後の、と。

    「だけで仕事"これを実現する唯一の方法は、優れたソリッドC++コードを書くことです。そして、あなたはそのツールを知っていて、使い方ではなく、"ねえ、shared_ptrはガベージコレクタのようなものです。それは?私はすべてのクエストを無視することができますオブジェクトライフタイムやメモリリークが発生した場合に使用します。

    +0

    叫んではいけない少し劇的ではありません:-)「あなたがしたいことはほんとうにありません」とは、人々の仮想耳にやや優しいかもしれません。そうでなければ私は完全に同意する。 –

    +0

    時には重大ではありません。 ;) – jalf

    +0

    実際には、どのように動作するかによって、ローカルオブジェクトへのポインタを渡しているときにshared_ptrが機能します。 Deleterをnull操作に設定し、それを行の下に送ることができます。あなたはもちろんshared_ptrについてのあらゆるセマンティックを壊していますが、ピンチではそのトリックを行います。 –

    1

    typedefについては、まったく同じ問題に対処するthis questionをご覧ください。

    第二部では、私はN3225の20.9.10.2/2に指定されているようstd::shared_ptrは、不完全なタイプのために使用することができるよう、あなたが間違っていると信じて:

    shared_ptr のテンプレートパラメータTがかもしれ不完全な型。

    現在の実装ではサポートされていない場合は、バグとみなす必要があります。

    +1

    私は宣言で不完全であることができますが、使用には不完全です。 shared_ptr は、T * – BatchyX

    +0

    とは異なり、Tのデストラクタに依存します。@BatchyX:デストラクタが使用されている時点で(deleteを使用した場合と同じように)、定義を使用できるようにする必要があります。そのような大きな変化ではありません。 –

    +0

    shared_ptrを使用するたびにデストラクタ呼び出しが行われるわけではありません。たとえば、コンテナから取り出されたポインタを返すだけの関数は、何も破壊する必要はなく、不完全な型で行うこともできます。このポインタをshared_ptrに置き換えると、この関数で削除を呼び出せなくても型が強制的に定義されます。 – BatchyX

    3

    どこでも使用できるスマートポインタを1つだけ選択しないでください。すべてのネジがフィリップスヘッドであるわけではありません。

    また、あなたは生のポインタとまったく同じ任意のスマートポインタの宣言前方に使用することができます。

    struct X; 
    
    ... 
    std::shared_ptr<X> ptr; // legal 
    
    struct X {}; 
    ptr = std::shared_ptr<X>(new X); // legal 
    

    をこれは私がSOにこの誤解を聞いた二度目であり、それは単に100%の偽です。

    1

    コードでは、あなたが次に従う "慣習"を持っていることがよくあり、チーム全体で一貫して行われている限り、人々はそれを理解することができます。

    共有ポインタのtypedefを「フォワード」ファイルで宣言する方がはるかに優れています。このような何か:

    namespace boost { template< typename T > class shared_ptr; } 
    
    namespace MyStuff 
    { 
        class Foo; 
        class Bar; 
        class Baz; 
    
        typedef boost::shared_ptr<Foo> FooPtr; 
        typedef boost::shared_ptr<Bar> BarPtr; 
        typedef boost::shared_ptr<Baz> BazPtr; 
    
    } 
    

    あなたは時々のshared_ptr以外のポインタを使用したいと思うでしょうが、あなたの大会は、その後のshared_ptrを意味XPTRと異なる表記を使用することです。

    もちろん、別の表記を使用することもできます。

    私たちは、たとえばshared_ptr<const Foo>

    を意味するweak_ptr<Foo> FooConstPtrを意味するFooWeakPtrを使うと思います。

    コーディング標準文書に記載されている必要があります。

    0

    私はそれにこの種のアプローチを好む:私は

    struct Foo : public Shared<Foo>, public Unique<Foo> 
    {}; 
    
    Foo::Ptr foo = Foo::shared(); 
    Foo::UPtr unique = Foo::unique(); 
    

    namespace Internal 
        { 
         template <class T> 
         struct DeclareShared 
         { 
         typedef std::shared_ptr<T> type; 
         }; 
    
         template <class T> 
         struct DeclareUnique 
         { 
         typedef std::unique_ptr<T> type; 
         }; 
        } 
    
        // Inherit one of these classes to use a generic smart pointer interface. 
    
        // If class is abstract, use this interface. 
        template <class T> 
        struct SharedAbstract 
        { 
         typedef typename Internal::DeclareShared<T>::type APtr; 
        }; 
    
        template <class T> 
        struct Shared 
        { 
         typedef typename Internal::DeclareShared<T>::type Ptr; 
    
         template <class... P> 
         static Ptr shared(P&&... args) 
         { 
         return std::make_shared<T>(std::forward<P>(args)...); 
         } 
        }; 
    
        template <class T> 
        struct UniqueAbstract 
        { 
         typedef typename Internal::DeclareUnique<T>::type AUPtr; 
        }; 
    
        template <class T> 
        struct Unique 
        { 
         typedef typename Internal::DeclareUnique<T>::type UPtr; 
    
         template <class... P> 
         static UPtr shared(P&&... args) 
         { 
         return std::unique_ptr<T>(new T(std::forward<P>(args)...)); 
         } 
        }; 
    

    は、スコープPTRのための複数のインタフェースを追加しましたなどとしたいと思い何でもあなたのようなことを行うことができVS10がどのようにバリデーショナルなテンプレートに対応しているかわからないが、少なくともGCC4.5以上ではうまく動作する。

    関連する問題