3

以下に示すようにノードクラスを作成し、それが二重リンクリストで使用された場合、二重リンクリストの分解時に無限ループが作成されますか?それともうまく終了しますか?

class Node
{
    Node(  );

    ~Node(  )
    {
       delete mNext; //deallocs next node
    }

    Contact mContact;
    Node* mPrevious;
    Node* mNext;
}; 

編集:コードをこれに変更した場合、それは機能しますか?

~Node(  )
{
   mPrevious = NULL;
   if (mNext->mPrevious != NULL)
   {
      delete mNext; //deallocs next node
   }
}

編集2:または、これが最適に機能しますか?

~Node(  )
{
   if (mPrevious != NULL)
   {
      mPrevious = NULL;
      delete mNext; //deallocs next node
   }
}
4

5 に答える 5

2

ノードがループを形成しているポインターを考慮するとmNext、ノードのいずれかを破壊すると、おそらく無限再帰ループが形成され、スタックを爆破してプログラムが終了します。

起こりそうなことは

  1. 最初の「外部」delete node;が発行されます。
  2. ノード デストラクタに入るときは、コード デストラクタが破棄プロセスで実行される「最初の」処理であるため、まだ何も実行されていません (破棄プロセス自体は非常に複雑で、デストラクタ コード、クラス変更、メンバーの破棄、ベースの破棄がこの順序で含まれます。詳細な説明については、この回答を参照してください)。
  3. 最初のデストラクタ命令が実行delete mNext;され、ループ内の次のノードで同じプロセスがトリガーされます。
  4. ノードがループを形成しているため、このチェーンはnode「後ろから」再び到達するため、最初の呼び出しが決して終了しない再帰になります。
  5. いずれにせよ、呼び出しごとにアクティベーション レコードにスタック スペースが割り当てられるため、しばらくすると、スタックに使用できるすべてのメモリが使い果たされ、OS がプロセスを強制終了します。デストラクタ コードが完了した後、メモリの割り当てを解除する必要があるため、削除呼び出しは「テール コール」ではありません。この再帰を簡単に最適化することはできませんdelete mNext;deleteオペレーターが完了します。

ただし、私の経験では、特別なコンパイラ オプションを使用しない限り、スタック オーバーフローはチェックされないため、プログラムの終了は非常に「異常」になることに注意してください。また、Windows では、プログラムの終了時に segfault エラーが発生した場合にそれを隠す恐ろしいコードがいくつかあることに注意してください。そのため、イベント ループを終了した後にこの操作が行われると、Windows プログラムが明らかに正常に終了する可能性が十分にあります。

スタック オーバーフローは通常は考慮されないため、明らかな「無限ループ」を含むあらゆる動作が可能です (この無限ループは再帰的デストラクタのものではなく、ランタイム システム内のどこかがスタック オーバーフローのために狂っている可能性があることに注意してください)。 .

なぜ私はおそらくという言葉を使ったのですか?その理由は、C++ 標準では、オブジェクトの複数の破壊は未定義の動作であると述べているためです。C++ では破壊を完了せずにデストラクタを終了する方法がないという事実にこれを追加すると、コンパイラは理論上、オブジェクトに「破壊されている」というフラグを立て、デーモンをオブジェクトから飛ばすことが許可されていることを理解できます。同じオブジェクトのデストラクタを 2 回入力すると、nosrils が発生します。ただし、このエラーのチェックは必須ではなく、コンパイラの作成者は怠け者であることが多く (これはプログラマにとって侮辱ではありません)、したがって、このチェックが存在する可能性は低いです (特別な追加のデバッグ オプションが有効になっている場合を除く)。

要約すると、永久にループできますか? はい。クラッシュできますか?もちろん。オブジェクトが 2 回破棄されているというメッセージを止めることはできますか? もちろん。プログラムを正常に終了できますか (つまり、エラー コードを設定せずに)?はい、それも。

何でも起れる。そしてマーフィーは、あなたに最も大きな損害を与えるものは何でも起こると言っています...たとえば、プログラムは開発中に毎回正常に終了します...そして、デモの日に直面するとひどくクラッシュします.千人の見込み客の前で。

それをしないでください:-)

于 2011-09-25T06:32:52.317 に答える
1

いつ停止するかを知る方法がないので、おそらく無限に実行されます。
おそらくList、(または実際の)へのポインタを持つクラスを作成する必要がありますNodeNodeのd'torは、この場合は自身のフィールドのみを処理する必要がありますmContactListのd'torは、リスト内のすべてのノードを反復処理し(いつ停止するかを覚えている)、各ノードを(正確に1回)削除する必要があります。

于 2011-09-25T05:57:00.220 に答える
1

mNext を null に初期化すると、無限に実行されることはありません。null ポインターに遭遇した場合、Delete は何もしません。したがって、期待どおりに終了します。

「以前の場合」オプションで何をしているのかわかりません。それらは機能しません。これは有効なノードであり、したがって前のノードがあるか、または有効なノードではなく、前のチェックの結果が未定義になるかのいずれかです。簡単な答えに固執します:

class Node 
{ 
Node(  mNext = NULL; ); 

~Node(  ) 
{ 
   delete mNext; //deallocs next node 
} 

Contact mContact; 
Node* mPrevious; 
Node* mNext; 
};  

明確化: このソリューションは機能しますが、次の 2 つの条件が満たされている場合に限ります。1) リストにノードが 2 回表示されない。2) リストは循環的ではありません。これらの条件を保証できる場合、これが最も簡単な答えです。それができない場合は、もっと複雑なことをする必要があります。

于 2011-09-25T06:25:29.160 に答える
0

Node個人的には、 aのデストラクタが他のノードと関係があるのは少し奇妙だと思います。

設計が私に任されていれば、オブジェクトへのポインター (および)Listを含むクラスを作成します。クラスのデストラクタは、リスト内のすべてのノードを繰り返し処理し、それらを破棄します。NodefirstlastList

于 2011-09-25T07:09:11.700 に答える
0

これは実際には簡単です。仮定 1) 二重リンク リストであり、循環リストではない 2) リンク リストにループがない: これは二重リンク リストである 3) 実装クラスには Node のインスタンスが 1 つしかない おそらく HeadNode または LinkList と呼ばれる ;) これが明示的に破棄されるノード


例: LinkList は 1->2->3->4->5->6->NULL です HeadNode のディスストラクタ呼び出し (3 番目の仮定を参照) は、次のように再帰呼び出しを引き起こします: delete(1)->delete(2 )->delete(3)->delete(4)->delete(5)->delete(6)->NULL したがって、(mNext != NULL) delete mNext の場合はチェックしてください :)


ただし: ノードを具体的に削除する場合: 上記の例で 4 つだけを削除するとします。すべてのノードは NULL になるまで削除されるため、削除する前に Mnext を NULL に設定してください。


ベスト プラクティスは、STL ライブラリを使用するか、問題の破壊部分にオートポインター クラスを使用することです。

于 2011-09-25T07:15:35.637 に答える