web-dev-qa-db-ja.com

オブジェクトが「スコープ外」になるのはいつですか?

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

言い換えれば、Node list_1によって指されている)は、この行の後にデストラクタを呼び出します。

Node* list_1 = new Node (3);

25
Pat Murray

最初に、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
49
sparc_spread
{ //scope is defined by the curly braces
    std::vector<int> vec;
}
// vec is out of scope here!
vec.Push_back(15);
5
Tony The Lion

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

現状のあなたの質問は、実装を見ずに答えることができません。このノードを宣言する場所になります。

void Foo()
{
    int i = 10;

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

} // i is out of scope
2
Ed S.

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

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

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

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

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

2
Kaz

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

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によって返されるpointerは、関数が終了するとスコープから外れますが、Node自体には何も起こりません。

void make_a_node() {
  Node* p = new Node;
} // Oh noes a memory leak!
2
DSimon