まず、C ++のオブジェクトは、スタックまたはヒープのいずれかに作成できることを忘れないでください。
スタックフレーム(またはスコープ)は、ステートメントによって定義されます。これは、関数と同じくらい大きくても、フロー制御ブロック(//など)と同じくらい小さくてもかまいwhile
ませif
んfor
。{}
コードの任意のブロックを囲む任意のペアもスタックフレームを構成します。プログラムがそのフレームを終了すると、フレーム内で定義されたローカル変数はスコープ外になります。スタック変数がスコープ外になると、そのデストラクタが呼び出されます。
したがって、これはスタックフレーム(関数の実行)とその中で宣言されたローカル変数の典型的な例です。スタックフレームが終了すると、関数が終了するとスコープから外れます。
void bigSideEffectGuy () {
BigHeavyObject b (200);
b.doSomeBigHeavyStuff();
}
bigSideEffectGuy();
// a BigHeavyObject called b was created during the call,
// and it went out of scope after the call finished.
// The destructor ~BigHeavyObject() was called when that happened.
if
スタックフレームがステートメントの本体にすぎない例を次に示します。
if (myCondition) {
Circle c (20);
c.draw();
}
// c is now out of scope
// The destructor ~Circle() has been called
フレームが終了した後、スタックで作成されたオブジェクトが「スコープ内にとどまる」唯一の方法は、それが関数の戻り値である場合です。ただし、オブジェクトがコピーされているため、これは実際には「スコープ内に残っている」わけではありません。そのため、オリジナルは範囲外になりますが、コピーが作成されます。例:
Circle myFunc () {
Circle c (20);
return c;
}
// The original c went out of scope.
// But, the object was copied back to another
// scope (the previous stack frame) as a return value.
// No destructor was called.
これで、オブジェクトをヒープ上で宣言することもできます。この議論のために、ヒープをメモリのアモルファスブロブと考えてください。スタックフレームに出入りするときに必要なメモリを自動的に割り当ておよび割り当て解除するスタックとは異なり、ヒープメモリを手動で予約および解放する必要があります。
ヒープ上で宣言されたオブジェクトは、ファッションの後、スタックフレーム間で「存続」します。ヒープ上で宣言されたオブジェクトがスコープから外れることは決してないと言うことができますが、それは実際には、オブジェクトが実際にはどのスコープにも関連付けられていないためです。このようなオブジェクトは、new
キーワードを介して作成する必要があり、ポインターによって参照される必要があります。
ヒープオブジェクトを使い終わったら、それを解放するのはあなたの責任です。delete
キーワードを使用してヒープオブジェクトを解放します。ヒープオブジェクトのデストラクタは、オブジェクトを解放するまで呼び出されません。
ヒープオブジェクトを参照するポインタは、通常、スコープに関連付けられたローカル変数です。ヒープオブジェクトの使用が終了したら、それを参照するポインタがスコープ外になることを許可します。ポインタが指しているオブジェクトを明示的に解放していない場合、プロセスが終了するまでヒープメモリのブロックが解放されることはありません(これはメモリリークと呼ばれます)。
考えてみてください。スタック上に作成されたオブジェクトは、部屋の椅子にテープで留められた風船のようなものです。部屋を出ると、バルーンが自動的に飛び出します。ヒープ上に作成されたオブジェクトは、部屋の椅子に結び付けられたリボン上の風船のようなものです。リボンがポインターです。部屋を出ると、リボンは自動的に消えますが、風船は天井に浮かぶだけでスペースを取ります。適切な手順は、ピンでバルーンをポップしてから部屋を出ると、リボンが消えます。しかし、紐の風船の良いところは、リボンをほどいて手に持って、部屋を出て風船を持って行くこともできることです。
したがって、リンクリストの例に進むと、通常、そのようなリストのノードはヒープ上で宣言され、各ノードは次のノードへのポインターを保持します。これらはすべてヒープ上にあり、スコープから外れることはありません。スコープ外になる可能性があるのは、リストのルートを指すポインター、つまり最初にリストを参照するために使用するポインターだけです。それは範囲外になる可能性があります。
ヒープ上に何かを作成し、ルートポインタがスコープ外になる例を次に示します。
if (myCondition) {
Node* list_1 = new Node (3);
Node* list_2 = new Node (4);
Node* list_3 = new Node (5);
list_1->next = list_2;
list_2->next = list_3;
list_3->next = null;
}
// The list still exists
// However list_1 just went out of scope
// So the list is "marooned" as a memory leak