web-dev-qa-db-ja.com

std :: vectorオブジェクトの割り当てを解除する「正しい」方法

最初の解決策は次のとおりです。

std::vector<int> *vec = new std::vector<int>;
assert(vec != NULL);
// ...
delete vec;

代替 は次のとおりです。

std::vector<int> v;
//...
vec.clear();
vec.swap(std::vector<int>(vec));

2番目の解決策はちょっとしたトリックです。それを行う「正しい」方法は何ですか?

更新:

私は、デストラクタがスタックから外れると呼び出されることを知っています。他のメソッドに興味がありました。

34
Jacob

ベクトルの割り当てを解除する最も簡単で信頼性の高い方法は、スタックで宣言し、単に何もしないことです。

void Foo() {
  std::vector<int> v;
  ...
}

C++は、メソッドの実行時にvのデストラクタが呼び出されることを保証します。 std::vectorのデストラクタは、割り当てたメモリがすべて解放されるようにします。 vector<T>Tタイプに適切なC++割り当て解除セマンティクスがあれば、すべてうまくいきます。

37
JaredPar

ベクトルオブジェクト自体を破壊せずに、ベクトル内のすべてのストレージの割り当てを解除する最も簡単な方法は、

vec = std::vector<int>();

2番目のバリアントにも同じ効果がありますが、途中でさらに多くの問題が発生します。 「コピーアンドスワップ」トリックは、ベクトル内の余分な容量の割り当てを解除し、保持するデータが含まれている場合に役立ちます。データがない場合は、コピーまたは交換する必要はありません。

15
Mike Seymour
std::vector<int> vi;
/*Push lots of stuff into the vector*/

// clean it up in C++03
// no need to clear() first
std::vector<int>().swap(vi);

// clean it up in C++0x
// not a one liner, but much more idiomatic
vi.clear();
vi.shrink_to_fit();
13
deft_code

マイク・シーモアがこれを試してみることに同意すると、最後のものがうまく機能していることに気付くでしょう

const int big_size = 10000;
vector<double> v( big_size );
cout << "Before clearing, the capacity of the vector is "
  << v.capacity() << " and its size is " << v.size();
v.clear();
cout << "\nAfter clearing, the capacity of the vector is "
  << v.capacity() << " and its size is " << v.size();
vector<double>().swap( v );

cout << "\nAfter swapping, the capacity of the vector is "
  << v.capacity() << " and its size is " << v.size();

vector<double> v1( big_size );
v1 = vector<double>();
cout << "\n After vector<double>();, the capacity of the vector is "
  << v1.capacity() << " and its size is " << v1.size();
3
Sam

本当に必要な場合以外は、メモリ割り当て関数を使用しないでください。クラスにベクターが必要な場合は、常にstd :: vectorメンバーを直接追加するだけです。ここでメモリを割り当てる必要はありません。

動的ベクトルが必要な場合、最初の例のように割り当てと削除を行うことは100%正しいです。

2番目の例では、std :: swapの呼び出しは厳密には必要ありません。clearメソッドはベクターをクリアして空にするためです。考えられる問題の1つは、ベクターが実際にメモリを解放し、それをオペレーティングシステム(またはランタイム)に戻す保証がないことです。ベクトルは、クリアした直後にベクトルを埋める場合に備えて、割り当てられたメモリを保持する場合があります。 std :: swapの呼び出しは、ベクターにその内部データを「強制的に」解放するトリックかもしれませんが、これが実際に起こるという保証はありません。

1
Patrick

ここでの私の推測は、大量のデータを一時的に含むベクターがあるということです。ベクトルがクリアされると、このメモリをすべて占有します。このメモリを使い終わったら解放したいが、作業している関数/オブジェクトが終了していない。

望ましさの降順のソリューション:

  1. 自然に破壊されるように、コードを使用するベクターが独自のブロック/関数/オブジェクト内にあるようにコードを修正します
  2. スワップトリックを使用すると、あらゆる状況でベクトルの割り当てが解除されることを心配する必要がなくなります。ライフタイムは、あなたがいるオブジェクト/関数に関連付けられます。
  3. ベクターを新規/削除します。これにより、前の方法よりも少し多くのメモリが解放されますが、メモリがリークしないことを確認することも難しくなります。

スワップと削除の唯一の技術的な違いは、ベースベクトル自体が破壊されないことです。これは小さなオーバーヘッドであり、心配する価値はありません(最終的にベクトルを破壊する限り)

より大きな考慮事項は、正しいコードの記述を容易にすることであり、スワップはそこに削除するよりも勝ると信じていますが、ベクトルを他の場所に移動するよりも悪いです。

1
Winston Ewert

ベクトルをスコープから外した場合、余分な作業なしで適切にクリーンアップされます。ベクトルがクラスのメンバー変数であり、所有者が破棄される前にその内容の割り当てを解除する場合は、vec.clear()を呼び出します。

ベクトルを保持しつつ、その内容を保持しているメモリの割り当てを解除する場合は、vec.swap(std::vector<int>());が実行します。 vecに保持したいアイテムが含まれていて、割り当てられたメモリを現在のsize()に近いサイズに縮小したい場合を除き、一時ファイルをコピー元にコピーする必要はありません。

0
Alan

ベクトルが本当にヒープ上にある必要がある場合は、次のことを忘れないでください:

std::auto_ptr<std::vector<int> > vec(new std::vector<int>);

次のようなコードで特に便利です。

std::auto_ptr<std::vector<int> > vec(vectorFactoryFunction());
0
Graphics Noob

この例では、動的期間とローカル期間の異なる種類のオブジェクトを扱っているため、これは有効な比較ではありません。デストラクタを呼び出すことができますORいずれかのスワップトリック(別名shrink_to_fit)を使用します。right方法は永続化するためにベクターオブジェクトが必要かどうか。

たとえば、永続化する必要がある場合があります参照またはそれへのポインタがある場合有効のままにする必要があります。この場合、割り当て方法に関係なく、縮小が唯一の方法です。

0
Cubbi

2番目の例がデフォルトのコンストラクターではなく、一時的なコピーコンストラクターを使用する理由はわかりません。これにより、コードの.clear()行を節約できます。

コンテナではない場合でも、任意のオブジェクトに対してこれをジェネリックにすることができます。ここでは、std :: swapがvector :: swapの呼び出しに特化されていると想定しています。

template<typename T>
void ResetToDefault(T & value)
{
    std::swap(T(), value);
}

std::vector<int> vec;  
//...  
ResetToDefault(vec);
0
Mark Ransom

Deleteはメモリの割り当てを解除し、メモリは次のオブジェクト用に解放されますが、ベクトルはなくなっています。

2番目のトリックは、余分なメモリをすべて解放しますが、ベクトルはそのまま残しますが、空のままにします。

0
Martin Beckett

どちらも機能しているように見えますが、ポインタでdeleteを呼び出さない理由はありません。ベクトルには、他のすべてを処理するデストラクタが必要です。

0
Ben313