私は管理された世界から来て、C ++の自動メモリ管理は私にとって非常に不明確です
私の理解が正しければ、ポインタをスタック オブジェクト内にカプセル化し、auto_ptr が範囲外になると、指定されたオブジェクトに対して自動的に削除を呼び出しますか?
どのような使い方をすればよいのでしょうか? また、C++ 固有の問題を自然に回避するにはどうすればよいでしょうか?
auto_ptr
C++でのRAIIの最も単純な実装です。あなたの理解は正しいです、そのデストラクタが呼び出されるときはいつでも、基礎となるポインタはdelete
dを取得します。
これは、デストラクタがなく、意味のあるRAIIが不可能なCからの一歩です。
automagicメモリ管理に向けた次のステップはshared_ptr
です。参照カウントを使用して、オブジェクトが生きているかどうかを追跡します。これにより、プログラマーはオブジェクトをもう少し自由に作成できますが、JavaやC#のガベージコレクションほど強力ではありません。このメソッドが失敗する1つの例は、循環参照です。AにBへの参照カウントポインタがあり、BにAへの参照カウントポインタがある場合、他のオブジェクトがどちらも使用していなくても、それらが破棄されることはありません。
現代のオブジェクト指向言語は、マークアンドスイープのある種のバリエーションを使用します。この手法により、循環参照の管理が可能になり、ほとんどのプログラミングタスクで十分な信頼性が得られます。
はい、範囲外になるとコンテンツをstd::auto_ptr
呼び出します。共有所有権が発生しない場合にのみdelete
使用します。で作成されたオブジェクトやその他のもので使用することはできません。auto_ptr
auto_ptr
new[]
共有所有権は通常、共有ポインタを使用してアプローチされます。たとえば、boostには実装があります。Boosts などで実装されている最も一般的な使用法は、参照カウントshared_ptr
方式を採用し、最後のスマート ポインターがスコープ外になったときにポインティング先をクリーンアップします。には大きな利点が 1 つあります。それは、カスタムのデリータを指定できることです。これにより、基本的にあらゆる種類のリソースをその中に入れることができ、使用する必要がある削除機能を指定するだけで済みます。
shared_ptr
スマートポインタの使い方は次のとおりです。例として、を使用しshared_ptr
ます。
{
shared_ptr<Foo> foo(new Foo);
// do things with foo
}
// foo's value is released here
ほとんどすべてのスマートポインターは、スマートポインターに保持されているオブジェクトがスマートポインターのスコープの最後で解放されるという点で、上記と同様のことを実現することを目的としています。ただし、広く使用されているスマートポインターには、次の3種類があり、所有権の処理方法に関するセマンティクスは大きく異なります。
shared_ptr
「共有所有権」を使用しshared_ptr
ます。複数のスコープ/オブジェクトが保持でき、それらはすべてオブジェクトへの参照を所有します。最後の参照が落ちると、オブジェクトは削除されます。これは、参照カウントを使用して行われます。auto_ptr
「譲渡可能な所有権」を使用します。auto_ptr
の値は1つの場所でのみ保持でき、が割り当てられるたびauto_ptr
に、譲受人はオブジェクトの所有権を受け取り、譲受人はオブジェクトへの参照を失います。オブジェクトauto_ptr
が別のオブジェクトに転送されずにスコープが終了するauto_ptr
と、オブジェクトは削除されます。オブジェクトの所有者は一度に1人だけなので、参照カウントは必要ありません。unique_ptr
/ scoped_ptr
「譲渡不可の所有権」を使用:オブジェクトは作成された場所でのみ保持され、他の場所に譲渡することはできません。プログラムunique_ptr
が作成されたスコープを離れると、オブジェクトは削除され、質問は行われません。取り入れることはたくさんあります、私は認めます、しかし私はそれがすべてすぐに沈むことを望みます。それが役に立てば幸い!
boost::shared_ptr
の代わりに使用する必要がありstd::auto_ptr
ます。
auto_ptr
ポインターのインスタンスをshared_ptr
保持するだけで、それらはローカル スタック オブジェクトであるため、スコープ外に出ると割り当てが解除されます。割り当てが解除されると、内部ポインターで delete が呼び出されます。
簡単な例、実際のshared_ptr
とauto_ptr
はより洗練されています (内部ポインターへの割り当てと変換/アクセスのためのメソッドがあります):
template <typename T>
struct myshrdptr
{
T * t;
myshrdptr(T * p) : t(p) {}
~myshrdptr()
{
cout << "myshrdptr deallocated" << endl;
delete t;
}
T * operator->() { return t; }
};
struct AB
{
void dump() { cout << "AB" << endl; }
};
void testShrdptr()
{
myshrdptr<AB> ab(new AB());
ab->dump();
// ab out of scope destructor called
// which calls delete on the internal pointer
// which deletes the AB object
}
別の場所から:
int main()
{
testShrdptr();
cout << "done ..." << endl;
}
次のように出力します (デストラクタが呼び出されていることがわかります):
AB
myshrdptr deallocated
done ...
ポインター型を提供する命令型言語 (C、C++、ADA など) を選択します。
その言語を再設計してポインター型を廃止し、代わりにプログラマーが再帰型を直接定義できるようにします。
コピー セマンティクスと参照セマンティクスの問題を慎重に検討してください。DrRacket を使用して言語のインタープリターを実装します。
ガベージコレクションされた参照との関係を理解しようとするのではなくauto_ptr
、根底にあるパターンを実際に確認する必要があります。
C++ では、すべてのローカル オブジェクトは、スコープ外に出るときにデストラクタが呼び出されます。これを利用して、メモリをクリーンアップできます。たとえば、コンストラクターでヒープ割り当てメモリへのポインターが与えられ、デストラクターでこのポインターを解放するクラスを作成できます。
それはほとんど何をするかauto_ptr
です。(残念なことに、auto_ptr
代入とコピーには、風変わりなセマンティクスで悪名高いものもあります)
またboost::shared_ptr
、他のスマート ポインターが行うことでもあります。それらのどれにも魔法はありません。それらはコンストラクターでポインターが与えられた単なるクラスであり、通常はスタック自体に割り当てられるため、ある時点で自動的にスコープ外になるため、ポインターを削除できるデストラクタが呼び出されます。最初にコンストラクターに渡しました。そのようなクラスを自分で書くことができます。ここでも魔法はなく、C++ のライフタイム ルールを単純に適用しただけです。ローカル オブジェクトがスコープ外になると、そのデストラクタが呼び出されます。
他の多くのクラスは仲介者を排除し、単純に同じクラスに割り当てと解放の両方を行わせます。たとえば、内部配列を作成するために必要に応じて呼び出し、デストラクタでそれstd::vector
を解放するために呼び出します。new
delete
ベクトルがコピーされると、新しい配列が割り当てられ、元の配列の内容がコピーされるため、各オブジェクトは独自のプライベート配列になります。
auto_ptr
、または一般的なスマート ポインターは、聖杯ではありません。それらはメモリ管理の問題を「解決」しません。これらはレシピの有用な部分の 1 つですが、メモリ管理のバグや頭の痛い問題を回避するには、基礎となるパターン (一般にRAIIとして知られている)を理解する必要があります。クリーンアップも担当する変数。
場合によっては、これはnew
自分自身を呼び出してメモリを割り当ててから結果を に渡すauto_ptr
ことを意味しますが、多くの場合、そもそも呼び出しnew
を行わないことを意味します。必要なオブジェクトをスタックに作成し、必要にnew
応じて内部で呼び出すようにします。あるいは、new
内部的に呼び出す必要さえないかもしれません。メモリ管理の秘訣は、ヒープ割り当てではなく、ローカルのスタック割り当てオブジェクトに依存することです。new
デフォルトでは使用しないでください。