web-dev-qa-db-ja.com

Cスタイルの配列のRAIIにshared_ptrを使用できますか?

私は、関数を早期に終了させる可能性のある多くの障害点があるコードのセクションに取り組んでいます。私が操作しているライブラリでは、Cスタイルの配列を関数に渡す必要があります。したがって、すべての出口点で配列に対してdeleteを呼び出す代わりに、次のようにします。

void SomeFunction(int arrayLength)
{
   shared_ptr<char> raiiArray(new char[arrayLength]);
   pArray = raiiArray.get();

   if(SomeFunctionThatRequiresCArray(pArray) == FAILED) { return; }

   //etc.
}

unique_ptrを使用したかったのですが、現在のコンパイラはそれをサポートしておらず、この場合、参照カウントのオーバーヘッドは実際には問題になりません。

レガシーコードとやり取りするときに、このプラクティスについて誰かが何か考えを持っているかどうか疑問に思っています。

[〜#〜] update [〜#〜]shared_ptrの代わりにdeleteを呼び出すdelete []を完全に忘れました。メモリリークは見られなかったので、それを使用することにしました。ベクトルを使うことすら考えていませんでした。最近、新しい(私にとって)C++を調べているので、「持っているツールがハンマーだけだと、すべてが釘のように見える」というケースがあると思います。症候群。フィードバックをお寄せいただきありがとうございます。

PDATE2質問を変更し、同じ間違いを犯した人にとってもう少し価値のあるものにするための回答を提供すると思いました。 scoped_arrayshared_arrayvectorのような選択肢がありますが、shared_ptrを使用して配列のスコープを管理できます(ただし、この後、なぜそうするのかわかりません。をしたい):

template <typename T>
    class ArrayDeleter
    {
    public:
        void operator () (T* d) const
        {
            delete [] d;
        }
    };

void SomeFunction(int arrayLength)
    {
       shared_ptr<char> raiiArray(new char[arrayLength], ArrayDeleter<char>());
       pArray = raiiArray.get();

       if(SomeFunctionThatRequiresCArray(pArray) == FAILED) { return; }

       //etc.
    }
27
bsruth

動的に割り当てられた配列へのポインタを保持するためにshared_ptrまたはscoped_ptrを使用しないでください。 shared_ptrとscoped_ptrは、delete ptr;を使用して、ポインターが参照されなくなったとき/スコープから外れたときにクリーンアップします。これにより、動的に割り当てられた配列で未定義の動作が呼び出されました。代わりに、shared_arrayまたはscoped_arrayを使用してください。これらは、破棄するときにdelete[] ptr;を正しく使用します。

質問に答えるには、スマートポインターを渡さない場合は、scoped_arrayよりもオーバーヘッドが少ないため、shared_arrayを使用します。

または、配列ストレージとしてstd::vectorを使用します(ベクトルは連続したメモリ割り当てを保証しています)。

27
Jon Benedicto

使用する boost::scoped_array、またはさらに良いstd::vector配列を扱っている場合。

15

単にstd::vectorを使用することを強くお勧めします。 vectorsの要素はヒープに割り当てられ、関数を終了する場所でvectorがスコープ外になると削除されます。

vectorをCスタイルの配列を必要とするレガシーコードに渡すには、単に&vectorName[0]を渡します。要素はメモリ内で連続していることが保証されています。

7
Justin Ardini

C++ 11ユーザー向けの注意事項:

shared_ptrの場合、C++ 11には、<memory>で定義され、標準に準拠した(最終ドラフトでの)配列型のデフォルトの削除機能があるため、次のような場合に追加の派手な削除機能なしで使用できます。

std::shared_ptr<char> raiiArray(new char[arrayLength], std::default_delete<char[]>()); 

C++ 11のunique_ptrには、new[]およびdelete[]を処理するための部分的な特殊化があります。しかし、残念ながら、共通の動作はありません。 shared_ptrにそのような専門分野がないのには十分な理由があるはずですが、私はそれを探しませんでした。ご存知の場合は、共有してください。

6
mcjoan

boost::scoped_ptr このため。

5

この

shared_ptr<char*> raiiArray(new char[arrayLength]);

これは良い習慣ではありませんが、演算子new[]で割り当てるため、未定義の動作が発生しますが、shared_ptroperator deleteを使用してメモリを解放します。使用する正しいことはboost::shared_arrayであるか、カスタム削除機能を追加することです。

3
jpalecek