32

C ++では、オブジェクトが「スコープ外」として定義されるのはいつですか?

より具体的には、単一リンクリストがある場合、単一のリストノードオブジェクトを「スコープ外」として定義するものは何でしょうか。または、オブジェクトが存在し、変数によって参照されている場合ptr、参照が削除された瞬間、または別のオブジェクトを指している瞬間に、オブジェクトは「範囲外」として定義されていると言うのは正しいですか?

更新:オブジェクトが実装されたデストラクタを持つクラスであると想定します。オブジェクトがスコープを出るとすぐにデストラクタが呼び出されますか?

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;
}

言い換えると、list_1この行の後にデストラクタを呼び出すことによってポイントされているノードは次のようになります。

Node* list_1 = new Node (3);

4

5 に答える 5

66

まず、C ++のオブジェクトは、スタックまたはヒープのいずれかに作成できることを忘れないでください。

スタックフレーム(またはスコープ)は、ステートメントによって定義されます。これは、関数と同じくらい大きくても、フロー制御ブロック(//など)と同じくらい小さくてもかまいwhileませiffor{}コードの任意のブロックを囲む任意のペアもスタックフレームを構成します。プログラムがそのフレームを終了すると、フレーム内で定義されたローカル変数はスコープ外になります。スタック変数がスコープ外になると、そのデストラクタが呼び出されます。

したがって、これはスタックフレーム(関数の実行)とその中で宣言されたローカル変数の典型的な例です。スタックフレームが終了すると、関数が終了するとスコープから外れます。

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
于 2012-04-09T23:10:18.103 に答える
5
{ //scope is defined by the curly braces
    std::vector<int> vec;
}
// vec is out of scope here!
vec.push_back(15);
于 2012-04-09T23:03:02.657 に答える
3

それが宣言されたスコープを離れるとき:)

現状のあなたの質問は、実装を見ずに答えることはできません。それはあなたがこのノードを宣言するところに帰着します。

void Foo()
{
    int i = 10;

    {
        int j = 20;
    } // j is out of scope

} // i is out of scope
于 2012-04-09T23:05:07.477 に答える
2

「範囲外」は換喩です。たとえば、1つの概念の名前または用語を使用して、密接に関連しているが異なるものについて話します。

C ++では、スコープはプログラムテキストの静的領域であるため、文字通り「スコープ外」とは、物理的にテキスト領域の外側を意味します。たとえば、{ int x; } int y;:の宣言は表示yされている範囲外xです。

「スコープ外に出る」という換喩は、あるスコープに関連付けられた環境の動的なアクティブ化/インスタンス化が終了しているという考えを表すために使用されます。そのため、そのスコープで定義された変数はなくなります(したがって「スコープ外」になります)。

実際に「スコープ外」になったのは、いわば命令ポインタです。プログラムの評価は現在、そのスコープを認識できないスコープで行われています。しかし、スコープ内のすべてがなくなるわけではありません!静的変数は、次にスコープが入力されたときに引き続き存在します。

「範囲外になる」はあまり正確ではありませんが、短く、誰もがそれが何を意味するのかを理解しています。

于 2012-04-09T23:05:52.953 に答える
2

関数内(または関数内の特定の中括弧括弧で囲まれた構造内)で宣言されたオブジェクトは、実行によってコードのその部分が離れるとスコープから外れます。

void some_func() {
  std::string x("Hello!");
  // x is in scope here
}
// But as soon as some_func returns, x is out of scope

これはスタックで宣言されたものにのみ適用されます。リストノードは通常、ヒープ上で。を使用してインスタンス化されるため、単一リンクリストとはほとんど関係ありませんnew

この例では、関数が終了すると、によって返されるポインターnewはスコープ外になりますが、ノード自体には何も起こりません。

void make_a_node() {
  Node* p = new Node;
} // Oh noes a memory leak!
于 2012-04-09T23:08:56.083 に答える