std::unique_ptr
たとえば、配列をサポートしています。
std::unique_ptr<int[]> p(new int[10]);
しかし、それは必要ですか?おそらく、std::vector
またはを使用する方が便利std::array
です。
その構成要素の用途はありますか?
std::shared_ptr
<T []>は現在c ++ 17です。
std::unique_ptr
たとえば、配列をサポートしています。
std::unique_ptr<int[]> p(new int[10]);
しかし、それは必要ですか?おそらく、std::vector
またはを使用する方が便利std::array
です。
その構成要素の用途はありますか?
std::shared_ptr
<T []>は現在c ++ 17です。
回答:
一部の人々はstd::vector
、アロケータを使用しても、を使用する余裕がありません。一部の人々は動的にサイズ設定された配列を必要とするのでstd::array
、外にあります。また、配列を返すことが知られている他のコードから配列を取得する人もいます。そのコードはvector
、何かを返すように書き直されることはありません。
を許可するとunique_ptr<T[]>
、それらのニーズに対応できます。
つまり、必要なunique_ptr<T[]>
ときに使用します。代わりの方法がうまくいかない場合。それは最後の手段のツールです。
vector
。それらが妥当な要件であるかどうかは議論できますが、それらが存在することを否定することはできません。
std::vector
もし使える人が使えない理由なんてありませんstd::unique_ptr
。
unique_ptr
どちらも使用しないため、この種のプロジェクトは実際に存在します。
トレードオフがあり、必要なものに一致するソリューションを選択します。私の頭の上から:
初期サイズ
vector
そして、unique_ptr<T[]>
サイズは、実行時に指定することを可能にしますarray
コンパイル時にのみサイズを指定できますサイズ変更
array
そして、unique_ptr<T[]>
リサイズはできません。vector
するストレージ
vector
unique_ptr<T[]>
オブジェクトの外(通常はヒープ)にデータを格納するarray
データをオブジェクトに直接格納しますコピーしています
array
vector
コピーを許可するunique_ptr<T[]>
コピーを許可しないスワップ/移動
vector
およびunique_ptr<T[]>
O(1)時間有しswap
、移動操作をarray
O(n)の時間swap
および移動操作があります。ここで、nは配列の要素数です。ポインター/リファレンス/イテレーターの無効化
array
オブジェクトが生きている間でも、ポインタ、参照、イテレータが無効化されないことを保証します。 swap()
unique_ptr<T[]>
イテレータはありません。ポインタと参照はswap()
、オブジェクトが生きている間だけ無効になります。(交換後、ポインターは交換した配列を指すので、その意味では「有効」です。)vector
再割り当てのポインタ、参照、イテレータを無効にする場合があります(再割り当ては特定の操作でのみ発生することが保証されています)。概念とアルゴリズムとの互換性
array
そしてvector
両方のコンテナですunique_ptr<T[]>
コンテナではありません私は認めざるを得ません。これは、ポリシーベースのデザインでリファクタリングする機会のように見えます。
vector
ます。次に、そのサイズまたは容量を増やしてvector
、再割り当てを強制します。その場合、そのイテレータ、ポインタ、または参照は、もはやのその要素を指していませんvector
。これが「無効化」の意味です。array
「再割り当て」がないため、この問題は発生しません。実際、細部に気づき、それに合わせて編集しました。
unique_ptr<T[]>
再割り当てがないため、無効化はできません。ただし、もちろん、配列がスコープ外になった場合でも、特定の要素へのポインタは無効になります。
T[]
、サイズ(または同等の情報)がどこかにぶら下がっている必要がありますoperator delete[]
。プログラマがそれにアクセスできればいいのですが。
aを使用する理由の1つunique_ptr
は、配列の値を初期化する実行時のコストを支払いたくない場合です。
std::vector<char> vec(1000000); // allocates AND value-initializes 1000000 chars
std::unique_ptr<char[]> p(new char[1000000]); // allocates storage for 1000000 chars
std::vector
コンストラクタとは、std::vector::resize()
初期値になるT
-しかし、new
場合があることしないだろうT
PODです。
C ++ 11のValue-Initialized Objectsおよびstd :: vectorコンストラクターを参照してください
ここでvector::reserve
は代替手段ではないことに注意してください:std :: vector :: reserve safeの後にrawポインターにアクセスしていますか?
これは、Cプログラマが選択する場合があります同じ理由ですmalloc
以上calloc
。
std::vector
するカスタムアロケーターを提供することstd::is_trivially_default_constructible
ですがstd::is_trivially_destructible
、厳密にはC ++標準に違反します(そのような型はデフォルトで初期化されていないため)。
std::unique_ptr
、多くのstd::vector
実装とは異なり、境界チェックは提供されません。
std::vector
標準での境界をチェックするために必要です.at()
。実装によっては、チェックインするデバッグモードもあるとおっしゃっていたと思います.operator[]
が、これは、移植性の高いコードを書くためには役に立たないと考えています。
std::vector
しながら、周りにコピーすることができるunique_ptr<int[]>
配列のユニークな所有権を発現することができます。std::array
一方、ではサイズをコンパイル時に決定する必要がありますが、状況によってはこれが不可能な場合があります。
unique_ptr
代わりにを使用するのと同じ理由で、これを防止することができますshared_ptr
。何か不足していますか?
unique_ptr
偶発的な誤用を防ぐだけではありません。また、よりも小さく、オーバーヘッドも低くなっていますshared_ptr
。重要なのは、「誤用」を防止するセマンティクスをクラスに含めるのは良いことですが、それだけが特定の型を使用する理由ではありません。そして、それがサイズを持っているという事実以外に理由がなければvector
、配列ストレージとしてはるかに有用です。unique_ptr<T[]>
vector
オーバーunique_ptr<T[]>
の可能なだけではなく、「あなたはそれをコピーすることはできません」ので、選ぶ、というのは、unique_ptr<T[]>
あなたがコピーをしたくないとき。誰かが間違ったことをするのを止めることは、必ずしもクラスを選ぶ最も重要な理由ではありません。
std::vector
aよりもオーバーヘッドが大きく、std::unique_ptr
1の代わりに3ポインタを使用します。 std::unique_ptr
コピーの構築をブロックしますが、移動の構築を有効にします。これは、意味上、処理中のデータが移動のみでコピーはできない場合class
、データを含むファイルに感染します。無効なデータに対する操作があると、実際にはコンテナクラスが悪化し、「使用しないでください」ではすべての罪が洗い流されることはありません。std::vector
クラスのすべてのインスタンスを手動で無効にする必要move
があるのは頭痛の種です。std::unique_ptr<std::array>
を持っていsize
ます。
スコット・マイヤーズはこれをEffective Modern C ++で言っています
存在
std::unique_ptr
配列のためには、あなただけに知的関心のあるべき理由std::array
、std::vector
、std::string
事実上、常に生の配列よりも優れたデータ構造の選択肢です。std::unique_ptr<T[]>
あなたが所有権を想定しているヒープ配列への生のポインタを返すCのようなAPIを使用しているときが、私が理解できる唯一の状況について考えられます。
Charles Salviaの答えは妥当だと思いますstd::unique_ptr<T[]>
。コンパイル時にサイズが不明な空の配列を初期化する唯一の方法です。スコット・マイヤーズはこの使用の動機について何を言わなければならないstd::unique_ptr<T[]>
でしょうか?
vector
stackoverflow.com/a/24852984/2436175よりも優先する理由として、効率性もあります。
私が使用しているunique_ptr<char[]>
ゲームエンジンで使用される事前に割り当てられたメモリプールを実装します。アイデアは、各フレームでメモリを割り当てたり解放したりせずに、衝突要求の結果やその他の物理学のようなものを返すために、動的割り当ての代わりに使用される事前割り当てされたメモリプールを提供することです。破棄ロジックを必要とせず(メモリの割り当て解除のみ)、ライフタイムが制限されたオブジェクト(通常は1、2、または3フレーム)を割り当てるためにメモリプールが必要なこの種のシナリオには、非常に便利です。
一般的なパターンは、一部の Windows Win32 API呼び出しで見つかりstd::unique_ptr<T[]>
ます。たとえば、一部のWin32 APIを呼び出すときに、出力バッファーの大きさが正確にわからない場合(内部にデータを書き込む)を使用すると便利です。そのバッファ):
// Buffer dynamically allocated by the caller, and filled by some Win32 API function.
// (Allocation will be made inside the 'while' loop below.)
std::unique_ptr<BYTE[]> buffer;
// Buffer length, in bytes.
// Initialize with some initial length that you expect to succeed at the first API call.
UINT32 bufferLength = /* ... */;
LONG returnCode = ERROR_INSUFFICIENT_BUFFER;
while (returnCode == ERROR_INSUFFICIENT_BUFFER)
{
// Allocate buffer of specified length
buffer.reset( BYTE[bufferLength] );
//
// Or, in C++14, could use make_unique() instead, e.g.
//
// buffer = std::make_unique<BYTE[]>(bufferLength);
//
//
// Call some Win32 API.
//
// If the size of the buffer (stored in 'bufferLength') is not big enough,
// the API will return ERROR_INSUFFICIENT_BUFFER, and the required size
// in the [in, out] parameter 'bufferLength'.
// In that case, there will be another try in the next loop iteration
// (with the allocation of a bigger buffer).
//
// Else, we'll exit the while loop body, and there will be either a failure
// different from ERROR_INSUFFICIENT_BUFFER, or the call will be successful
// and the required information will be available in the buffer.
//
returnCode = ::SomeApiCall(inParam1, inParam2, inParam3,
&bufferLength, // size of output buffer
buffer.get(), // output buffer pointer
&outParam1, &outParam2);
}
if (Failed(returnCode))
{
// Handle failure, or throw exception, etc.
...
}
// All right!
// Do some processing with the returned information...
...
std::vector<char>
これらの場合にのみ使用できます。
私はstd::unique_ptr<bool[]>
HDF5ライブラリ(効率的なバイナリデータストレージ用のライブラリで、科学でよく使用されている)にあるを使用しなければならない場合に直面しました。一部のコンパイラー(私の場合はVisual Studio 2015)は、std::vector<bool>
(各バイトで8つのブール値を使用して)圧縮を提供します。これは、HDF5のような、その圧縮を気にしない問題です。を使用するとstd::vector<bool>
、その圧縮のためにHDF5は最終的にゴミを読み取っていました。
std::vector
うまくいかなかった場合、救助のためにそこに誰がいて、動的配列をきれいに割り当てる必要があると思いますか?:-)
一言で言えば、これは、最もメモリ効率が高いことです。
Aにstd::string
は、ポインター、長さ、および「short-string-optimization」バッファーが付属しています。しかし、私の状況は、ほとんど常に空の文字列を、何十万もの構造に格納する必要があるということです。Cでは、私はを使用するだけchar *
で、ほとんどの場合nullになります。これはC ++でも機能しますが、a char *
にはデストラクタがなく、自分自身を削除する方法がわかりません。対照的に、a std::unique_ptr<char[]>
はスコープ外になると自分自身を削除します。空std::string
は32バイトを占有しますが、空std::unique_ptr<char[]>
は8バイトを占有します。つまり、ポインタのサイズとまったく同じです。
最大の欠点は、文字列の長さを知りたいときはいつでも、strlen
それを呼び出さなければならないということです。
Deviceでメモリを割り当てるときに、GPUでのCUDAプログラミングのケースのvector
代わりに「使用する必要がある」と考える人に答えるunique_ptr
には、ポインタ配列(を使用cudaMalloc
)に移動する必要があります。次に、Hostでこのデータを取得するときは、もう一度ポインターを取得する必要があり、ポインターをunique_ptr
簡単に処理できます。double*
への変換の追加コストvector<double>
は不要であり、パフォーマンスの低下につながります。
を許可して使用するもう1つの理由std::unique_ptr<T[]>
は、これまでのところ応答で言及されていません。これにより、配列要素の型を前方宣言できるようになります。
これは、連鎖を最小限にしたい場合に便利です。 #include
、ヘッダー内ステートメント(ビルドのパフォーマンスを最適化するため)。
例えば -
myclass.h:
class ALargeAndComplicatedClassWithLotsOfDependencies;
class MyClass {
...
private:
std::unique_ptr<ALargeAndComplicatedClassWithLotsOfDependencies[]> m_InternalArray;
};
myclass.cpp:
#include "myclass.h"
#include "ALargeAndComplicatedClassWithLotsOfDependencies.h"
// MyClass implementation goes here
上記のコード構造により、で必要な内部実装の依存関係を含める必要なしに、誰でも#include "myclass.h"
を使用できますMyClass
。MyClass::m_InternalArray
ます。
m_InternalArray
がstd::array<ALargeAndComplicatedClassWithLotsOfDependencies>
、またはとしてstd::vector<...>
それぞれ宣言されている場合-結果は、コンパイル時エラーである不完全な型の使用が試行されます。
受け入れられた答えの精神に強く反対することはできません。「最後の手段」?それとは程遠い!
私の見たところ、Cおよび他のいくつかの同様の言語と比較してC ++の最も強力な機能の1つは、制約を表現して、コンパイル時にチェックし、偶発的な誤用を防ぐことができることです。そのため、構造を設計するときは、どのような操作を許可するかを自問してください。他のすべての使用は禁止する必要があります。そのような制限を静的に(コンパイル時に)実装して、誤用が原因でコンパイルエラーが発生するようにするのが最善です。
したがって、配列が必要な場合、次の質問への回答でその動作を指定します。1。サイズはa)実行時に動的、またはb)静的ですが、実行時にのみ既知、またはc)静的でコンパイル時に既知ですか?2.配列をスタックに割り当てることはできますか?
そして答えに基づいて、これはそのような配列に最適なデータ構造であると私が見ているものです:
Dynamic | Runtime static | Static
Stack std::vector unique_ptr<T[]> std::array
Heap std::vector unique_ptr<T[]> unique_ptr<std::array>
そうだね unique_ptr<std::array>
も考慮する必要があると。どちらも最終手段ではありません。アルゴリズムに最適なものを考えてください。
これらはすべて、データ配列への生のポインタ(vector.data()
/ array.data()
/ uniquePtr.get()
)を介してプレーンC APIと互換性があります。
PS上記の考慮事項とは別に、所有権も1つstd::array
ありstd::vector
ます。値のセマンティクス(値によるコピーと受け渡しをネイティブでサポート)があり、unique_ptr<T[]>
移動のみが可能(単一の所有権を適用)です。どちらも、さまざまなシナリオで役立ちます。逆に、単純な静的配列(int[N]
)と単純な動的配列(new int[10]
)はどちらも提供しないため、可能であれば回避する必要があります。これは、大多数のケースで可能です。それだけでは不十分な場合、単純な動的配列でもサイズを問い合わせる方法がありません。メモリの破損やセキュリティホールが発生する可能性があります。
ハッチの反対側で "キャッチ"された後の有効期間の測定値を持つ既存のAPI(ウィンドウメッセージまたはスレッド関連のコールバックパラメーターを考える)を介して単一のポインターを突くだけの場合、これらは可能な最も正しい答えである可能性があります。しかし、これは呼び出しコードとは無関係です:
unique_ptr<byte[]> data = get_some_data();
threadpool->post_work([](void* param) { do_a_thing(unique_ptr<byte[]>((byte*)param)); },
data.release());
私たちは皆、自分たちにとっていいものになりたいと思っています。C ++はそれ以外の場合のためのものです。
unique_ptr<char[]>
CのパフォーマンスとC ++の利便性が必要な場所で使用できます。数百万(信頼できない場合は数十億)の文字列を操作する必要があるとします。それらのそれぞれを別々にstring
またはvector<char>
オブジェクトオブジェクト、メモリ(ヒープ)管理ルーチンの障害になります。特に、異なる文字列を何度も割り当てて削除する必要がある場合。
ただし、その数の文字列を格納するために単一のバッファを割り当てることができます。char* buffer = (char*)malloc(total_size);
明らかな理由で気に入らない場合(明らかでない場合は、「スマートptrを使用する理由」を検索してください)。あなたはむしろしたいですunique_ptr<char[]> buffer(new char[total_size]);
類推すると、同じパフォーマンスと利便性の考慮事項が非char
データにも適用されます(数百万のベクトル/行列/オブジェクトを考慮してください)。
vector<char>
か?答えは、おそらくバッファを作成するときにゼロで初期化されるためだと思いますが、を使用する場合はそうではありませんunique_ptr<char[]>
。しかし、この重要なナゲットはあなたの答えから欠落しています。
new[]
std::vector
たとえば、不注意なプログラマが誤ってコピーを導入するのを防ぐために、を使用することに対する一般的なルールがあります。C ++コンテナは、ポインタを使用したローリング独自のものよりも優先されるという一般的なルールがあります。これは原則です。例外があります。さらにあります。これらは単なる例です。
std::shared_ptr<T[]>
、がないことを指摘する必要がありますが、誰かが提案を書くことに迷惑をかける可能性がある場合は、C ++ 14にあるはずです。その間、常にありboost::shared_array
ます。