ポインタを削除するためにキーワード「delete」を使用しない場合のメモリリークの正式な意味を知っているだけです。しかし、次のように実行すると、なぜリークの可能性があるのでしょうか?
void k()
{
vector<m*>p;
:
:
:
}
私が知る限り、ポインタの削除はコンパイラ自体によって自動的に行われるので、最後にポインタを削除する必要は本当にありますか?
私の知る限り、ポインタの削除はコンパイラ自体によって自動的に行われます
いいえ、ちがいます。以下は漏れです。
vector<m*>p;
p.push_back(new m);
自分でメモリを削除する必要があります。
delete p[0]; //in this case
クリーンな代替手段は、スマート ポインターを使用することです。
vector<std::shared_ptr<m> > p;
これは、何を入れるかによって異なりますp
。他の場所で管理されているもの (または動的に割り当てられていないもの) へのポインタを格納するだけであれば、リークはありません。 の内容はvector
デストラクタによってクリーンアップされます。
ただし、次のようなことを行い、 before exitsの要素をp.push_back(new m);
呼び出さない場合は、実際にメモリ リークが発生しています。(ポインター)の内容は、そのデストラクタによってクリーンアップされます。ポインタが指すメモリはそうではありません。delete
p
k
vector
最初の質問に答えるために: メモリ管理エラーには、関連しているが異なる概念が 2 つあります。私は、一般的なメモリ デバッガである Valgrind で使用される用語が好きです。
まだ到達可能なメモリ: これは厳密にはリークではなく、プログラマの怠慢です。汚れていますが、必ずしも問題ではありません。これは、クリーンアップできたはずのものをクリーンアップすることを気にしない場合に発生します。
int main()
{
int * p = new int[100];
}
// memory at p is still reachable
このようなコードでは、「まだ到達可能な」割り当ては通常、プログラムの最後まで使用され、クリーンアップする唯一の適切なポイントは最後になります。
間違いなく失われたメモリ: これはあなたの本当のリークです. この状況は、動的に割り当てられたメモリへの参照がすべて失われた場合に発生します。つまり、原則として、それらのリソースを解放する方法はありません。これは重大なプログラミング エラーです。ループ内、または任意の回数呼び出されたものでメモリ リークが発生した場合、プログラムは長時間実行された後、おそらく OS の残りの部分がしばらく使用できなくなる前に停止する可能性があります。典型的なコードは次のようになります。
int foo()
{
int * p = new int[100]();
return p[20] + 2;
} // leak: We lost track of the memory at p
int main()
{
for (std::string line; std::getline(std::cin, line); )
{
int * q = new int[line.length() + 1];
q[0] = foo(); // leak 1
} // leak 2: lost track of the memory at q
}
上記の状況はすべて、13 世紀に最初に策定されたメモリ管理の基本規則に違反しています。
CPU、私をあなたのコードの道具にしてください。
あるところはmalloc
書きましょうfree
。
あるところにnew
、delete
。
あるところにnew[]
、delete[]
。
入る
当然の結果として、 C++ メモリ管理のゴールデン ルールを推測できます。これにより、メモリ管理のバグのリスクが劇的に減少します。
new
使用しないでくださいdelete
。生のポインターを使用しないでください。
要点は、生のポインタは可能なポインティに関する所有権情報を持たないということです。したがって、生のポインターだけに基づいて、クリーンアップを行う必要があるかどうか、およびその方法を決定することは不可能です。これはコードの問題でもあります。答えは単純に「場合による」です。
ベクトルの内容 ( s )を割り当てると仮定すると、リークを回避したい場合は、関数を終了する前に自分で実行する必要があります。ベクトル コンテンツの自動削除は、コンテンツがオブジェクトであり、動的に割り当てられたオブジェクトへのポインタではない場合にのみ機能します。m*
new
delete
あなたができることは、に置き換えることm*
ですstd::unique_ptr<m>
。これで、スマート ポインターが自動的に実行delete
されるようになり、何もする必要はありません。
vector<std::unique_ptr<m>>p;
あるいは、これも安全です。値で保存しているため、関数が終了すると、内容はベクトル デストラクタによって自動的に破棄されます。
vector<m>p;
表面的な答えは「いいえ」です。スニペットにメモリを割り当てていないため、リークが発生することはありません。より使いやすい答えは次のとおりです。それは、の役割と、vector
何を入れているかによって異なります。
まず、型m
に値のセマンティクスがあり、コピー可能である場合は、vector<m>
ではなくを使用する必要がありvector<m*>
ます。そしてもちろん、リークの可能性はありません(正しくvector<m>
実装したと仮定して)。m
ポインターのベクトルを使用する最も一般的な理由は、少なくとも私が携わったアプリケーション ドメインでは、エンティティ オブジェクト間のナビゲーションです。各エンティティ オブジェクトは、独自の有効期間を管理するか、(あまり頻繁ではありませんが) 有効期間を管理する他のエンティティ オブジェクトに属します。したがって、リークのリスクはありませんが、ダングリング ポインターの非常に重大なリスクがあります。オブジェクトを指すオブジェクトの有効期間が終了したら、そのオブジェクトへのポインターがベクトルから削除されていることを確認する必要があります。(これは通常、オブザーバー パターンの変形を使用して行われます。)
ポインターのベクトルを使用するもう 1 つの理由は、含まれているオブジェクトがポリモーフィックであるか、コピーするのに大きくてコストがかかるためです。このような場合、含まれているオブジェクトにポインターが含まれていない場合、または循環が不可能であることを証明でき、ポイントされたオブジェクトに重要な内部動作がないことを証明できる場合 (したがって、の使用は非常に限られていますthis
)、のコレクションstd::shared_ptr<m>
; 実際には、Boost のポインター コンテナーがおそらくより良い解決策です (そして のリスクを回避しますstd::shared_ptr
)。ソリューションと Boost のポインター コンテナーの両方
shared_ptr
で、ベクター内のすべての要素が動的に割り当てられる必要があります。
ベクトル自体は問題ありません。独自のメモリを自動的かつ正しく管理します。
したがって、答えはそのベクトルに何を入れるかによって異なります。ヒープに割り当てられたオブジェクトへのポインターを配置する場合、それらのオブジェクトを使い終わったときにそれらのオブジェクトの割り当てを解除するのはあなたの責任です。ベクトルはそれをしません。
これを怠ると、メモリ リークが発生します。
そのベクトルから要素を削除する場合 ( resize
、erase
またはを使用)、それが で割り当てられてclear
いると仮定すると、演算子は使用されません。したがって、メモリリークが発生します。ただし、これらのポインタがスタック上の変数を指している場合、メモリ リークは発生しません。m*
new
delete
編集: ベクトルから要素を消去する前に要素を削除できますが、サイズ変更またはクリアを呼び出すと、すべてを削除することはほとんどありません (クリアする前にループして削除しない限り)。
ベクトルが削除されても、ベクトル内のポインターは削除されません。あなたは使用することができます
vector<shared_ptr<m>> p
プログラムの他の部分とポインターを共有する場合。