web-dev-qa-db-ja.com

C ++によるベクター、オブジェクト、空きメモリの削除

私は、C++での事柄の削除に関して完全に混乱しています。オブジェクトの配列を宣言し、clear()メンバー関数を使用する場合。メモリが解放されたことを確認できますか?

例えば ​​:

tempObject obj1;
tempObject obj2;
vector<tempObject> tempVector;

tempVector.pushback(obj1);
tempVector.pushback(obj2);

Clearを安全に呼び出して、すべてのメモリを解放できますか?または、1つずつ削除するために繰り返し処理する必要がありますか?

tempVector.clear();

このシナリオがオブジェクトのポインターに変更された場合、答えは上記と同じになりますか?

vector<tempObject> *tempVector;
//Push objects....
tempVector->clear();
46
mister

Clearを呼び出すと、すべてのオブジェクトが破壊されますが、メモリは解放されません。個々の要素をループしても効果はありません(オブジェクトに対してどのアクションを実行することを提案しますか?)これを行うことができます:

vector<tempObject>().swap(tempVector);

これにより、メモリが割り当てられていない空のベクターが作成され、tempVectorとスワップされて、メモリの割り当てが事実上解除されます。

C++ 11には、関数shrink_to_fitもあり、clear()の呼び出し後に呼び出すことができ、理論的にはサイズ(現在は0)に合わせて容量を縮小します。ただし、これは拘束力のない要求であり、実装では自由に無視できます。

80

ここには、2つの別個のものがあります。

  1. オブジェクトの寿命
  2. 保管期間

例えば:

{
    vector<MyObject> v;
    // do some stuff, Push some objects onto v
    v.clear(); // 1
    // maybe do some more stuff
} // 2

1で、vをクリアします。これにより、格納していたすべてのオブジェクトが破壊されます。あなたが書いた場合、それぞれがデストラクタが呼び出され、そのMyObjectが所有するものはすべてリリースされます。 ただし、vector vには、後で必要に応じてrawストレージを保持する権利があります。

12の間でさらに多くのものをプッシュすることにした場合、古いメモリを再利用できるため、時間を節約できます。

2で、ベクトルvは範囲外になります。1以降にプッシュしたオブジェクトは破棄されます(明示的にclearを再度呼び出したかのように)ストレージも解放されます(vは再利用できなくなります)。


vがポインターになるように例を変更した場合、2でスコープ外に出たポインターはそれを行わないため、明示的に削除する必要があります。その場合はstd::unique_ptrのようなものを使用する方が良いですが、そうしないとvがリークされると、割り当てられたストレージもリークされます。上記のように、vが削除されていることを確認する必要があり、clearを呼び出すだけでは不十分です。

22
Useless

vector::clear()は、オブジェクトを格納するためにベクターによって割り当てられたメモリを解放しません。保持するオブジェクトのデストラクタを呼び出します。

たとえば、ベクターが配列をバッキングストアとして使用し、現在10個の要素を含んでいる場合、clear()を呼び出すと、配列内の各オブジェクトのデストラクターが呼び出されますただし、バッキング配列は割り当て解除されません、したがって、ベクターに割り当てられたsizeof(T) * 10バイトがまだあります(少なくとも)。 size()は0になりますが、size()はベクター内の要素数を返します。必ずしもバッキングストアのサイズではありません。

2番目の質問については、newで割り当てるものはすべて、deleteで割り当てを解除する必要があります。通常、この理由でベクターへのポインターを維持しません。これを行う正当な理由はめったにない(もしあったとしても)ため、ベクトルがスコープを離れるときにベクターがクリーンアップされるのを防ぐことができます。ただし、clear()を呼び出すと、割り当て方法に関係なく同じように動作します。

14
Ed S.

ベクトルを繰り返し使用する必要があり、現在のコードがループ内または関数呼び出しごとに繰り返し宣言している場合、メモリが不足する可能性があります。外部で宣言し、関数のポインターとして渡し、使用することをお勧めします。

my_arr.resize()

これにより、毎回新しいシーケンスを要求するのではなく、ベクターに同じメモリシーケンスを使用し続けます。これが役に立てば幸いです。注:サイズを異なるサイズに変更すると、ランダムな値が追加される場合があります。必要に応じて、0などの整数を渡して初期化します。

0
user3715882

clear()メンバー関数を使用する場合。メモリが解放されたことを確認できますか?

いいえ、clear()メンバー関数はベクターに含まれるすべてのオブジェクトを破棄しますが、ベクターの 容量 は変更しません。ベクトルのサイズには影響しますが、容量には影響しません。

ベクトルの容量を変更する場合は、 clear-and-minimize idiom を使用できます。つまり、(一時的な)空のベクトルを作成してから、両方のベクトルを交換します。


各アプローチが容量にどのように影響するかを簡単に確認できます。渡されたベクトルでclear()メンバー関数を呼び出す次の関数テンプレートを検討してください。

template<typename T>
auto clear(std::vector<T>& vec) {
   vec.clear();
   return vec.capacity();
}

次に、渡されたベクトルを空のベクトルと交換する関数テンプレートempty_swap()を考えます。

template<typename T>
auto empty_swap(std::vector<T>& vec) {
   std::vector<T>().swap(vec);
   return vec.capacity();
}

両方の関数テンプレートは、返される瞬間にベクトルの容量を返し、その後:

std::vector<double> v(1000), u(1000);
std::cout << clear(v) << '\n';
std::cout << empty_swap(u) << '\n';

出力:

1000
0
0
El Profesor