1. ホーム
  2. c++

[解決済み] shared_ptr to an array : 使うべきですか?

2022-04-13 16:31:37

質問

に関するちょっとした質問です。 shared_ptr .

を使用するのは良い習慣ですか? shared_ptr は配列を指しているのですか? 例えば

shared_ptr<int> sp(new int[10]);

そうでないとしたら、なぜそうしないのか?私がすでに知っている理由のひとつは、このメソッドでは shared_ptr . したがって、配列への通常のポインタのようには使用できません。

どのように解決するのですか?

C++17 , shared_ptr は、動的に割り当てられた配列を管理するために使用することができます。そのため shared_ptr この場合のテンプレート引数は T[N] または T[] . ですから、次のように書くことができます。

shared_ptr<int[]> sp(new int[10]);

n4659より。 [util.smartptr.shared.const]です。

  template<class Y> explicit shared_ptr(Y* p);

必要です。 Y は完全な型でなければならない。式は delete[] p の場合 T が配列型である場合、または delete p の場合は T が配列型でない場合、明確に定義された動作を持ち、例外を投げてはならない。

...

備考 いつ T が配列型でない限り、このコンストラクタはオーバーロードの解決に関与しない。 delete[] p が整形式で、かつ TU[N]Y(*)[N] は変換可能です。 T* または TU[]Y(*)[] は変換可能です。 T* . ...

これに対応するため、メンバータイプ element_type が定義されるようになりました。

using element_type = remove_extent_t<T>;

配列要素へのアクセスは operator[]

  element_type& operator[](ptrdiff_t i) const;

必要です。 get() != 0 && i >= 0 . もし TU[N] , i < N . ...

備考 いつ T が配列型でない場合、このメンバ関数が宣言されているかどうかは不定です。宣言されている場合は、関数の宣言(必ずしも定義ではない)が適切に形成されていなければならないことを除いて、その戻り値の型は不定である。


C++17以前 , shared_ptr かもしれない ない は、動的に割り当てられた配列を管理するために使用されます。デフォルトでは shared_ptr が呼び出されます。 delete は、管理対象オブジェクトへの参照がなくなったときに、そのオブジェクトに適用されます。しかし new[] を呼び出す必要があります。 delete[] であって delete を使用することで、リソースを解放することができます。

を正しく使用するために shared_ptr を配列で使用する場合は、独自のデレタが必要です。

template< typename T >
struct array_deleter
{
  void operator ()( T const * p)
  { 
    delete[] p; 
  }
};

shared_ptrを以下のように作成します。

std::shared_ptr<int> sp(new int[10], array_deleter<int>());

現在 shared_ptr が正しく呼び出されます。 delete[] は、管理対象オブジェクトを破棄する際に

上記のカスタムデレタは、以下のように置き換えることができます。

  • その std::default_delete 配列型に対する部分的な特殊化

    std::shared_ptr<int> sp(new int[10], std::default_delete<int[]>());
    
    
  • ラムダ式

    std::shared_ptr<int> sp(new int[10], [](int *p) { delete[] p; });
    
    

また、実際に管理対象オブジェクトの共有権限を必要としない限りは unique_ptr は、配列型に対する部分的な特殊化を持っているので、このタスクにより適しています。

std::unique_ptr<int[]> up(new int[10]); // this will correctly call delete[]


C++ Extensions for Library Fundamentalsによって導入された変更点

上に挙げたものに代わるものとして、C++17以前には ライブラリファンダメンタルズ技術仕様書 を拡張したものです。 shared_ptr は、オブジェクトの配列を所有する場合に、すぐに動作するようにするためのものです。現在の shared_ptr このTSで予定されている変更は、以下のサイトにあります。 N4082 . これらの変更点は std::experimental 名前空間に含まれ <experimental/memory> ヘッダを表示します。をサポートするための関連するいくつかの変更点 shared_ptr を配列のために使用することができます。

- メンバー型の定義 element_type 変更

typedef T element_type;

 typedef typename remove_extent<T>::type element_type;

- メンバー紹介 operator[] が追加されています。

 element_type& operator[](ptrdiff_t i) const noexcept;

- とは異なり unique_ptr 配列の部分的な特殊化で、両方とも shared_ptr<T[]>shared_ptr<T[N]> は有効で、どちらも結果的に delete[] は、管理されたオブジェクトの配列に対して呼び出されます。

 template<class Y> explicit shared_ptr(Y* p);

必要なもの : Y は完全な型でなければならない。式は delete[] p の場合 T が配列型である場合、または delete p の場合は T が配列型でない場合、整形式であり、明確に定義された動作を持ち、例外を投げてはならない。また,例外を発生させてはならない。 TU[N] , Y(*)[N] に変換できるものとする。 T* の場合。 TU[] , Y(*)[] に変換できるものとする。 T* それ以外の場合は Y* に変換されるものとする。 T* .