5

重複の可能性:
オブジェクトを削除したのに、以下のコードがクラッシュしないのはなぜですか?

今日、私は C++ メモリ管理について何も知らないことがわかりました。このコードを見てください:

class A
{
 public:
     A(){std::cout << "constructor called" << this << std::endl;}
    ~A(){std::cout << "destructor called" << this << std::endl;}
      void test (){std::cout << "test" << this << std::endl;}
 };

 int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

A *aa = new A();

delete aa;
aa->test();
aa->test();
aa->test();
std::cout << "still works\n";
return a.exec();
}

なぜクラッシュしないのですか?デストラクタが呼び出されたにもかかわらず実行を続けるのはなぜですか? メソッドを呼び出すtestと、アプリに属さないメモリを処理します。

aa = NULL;さらに驚くべきことは、の直後に挿入しても機能することですdelete aa;testメソッドは問題なく呼び出されます。私は完全に混乱していることを告白する必要があります。デストラクタの目的と、効果がない場合に NULL を割り当てる目的は何ですか?

4

3 に答える 3

10

なぜこれが機能するのですか?

あなたの質問に答えるには、次の 2 つの方法があります。

技術的な回答:

あなたのコードにはUndefined Behaviorがあります。aまたはed ポインター
を逆参照します。C++ 標準に従って、どちらも未定義の動作を呼び出します。それが機能するかどうかは無意味です。 未定義の動作とは、任意の動作が可能であり、クラッシュする場合としない場合があることを意味しますが、プログラムが適切に定義された出力を提供することを期待できないことを意味します。これは単に、あらゆる動作が可能であり、一貫性がなく、明確に定義されていない可能性があることを意味します。NULLdelete

実用的な答え:

thisメンバー関数の呼び出し中にコンパイラが実際に逆参照しないため、クラッシュしません。関数が関数でない限り、コンパイラは、最初のパラメーターとしてvirtual関数に渡すことによって、メンバー関数呼び出しを通常の関数呼び出しに変換します。thisこれは、コンパイラがコンパイル時に呼び出す関数を正確に決定できるためです。実際には、deleted または pointer を介してメンバー関数を呼び出しても、 (またはedの場合は無効です) をNULL逆参照しません。さらに、関数本体内で何らかのメンバーがアクセスされた場合にのみ、 が逆参照されます。 あなたの場合、関数本体内のメンバーにアクセスすることはないため、クラッシュしません。thisNULLdeletethis

メンバーを追加して関数内で逆参照すると、間違いなくクラッシュするはずです。

とにかく、実用的な答えで言われていることの技術的な答えはすべての上にあり、標準はそれを言っているので。

于 2012-12-23T14:24:13.773 に答える
3

なぜクラッシュしないのですか?

削除されたポインターを逆参照することにより、未定義の動作を呼び出しています。未定義の動作は、プログラムが何でもできることを意味します。クラッシュしないことは何でも含まれます。

未定義の動作が常にプログラムがすぐにクラッシュすることを意味する場合、デバッグと修正は簡単です。未定義の動作に関する最も厄介な問題の 1 つは、プログラムをテストすると正しく動作しているように見えても、それを顧客に出荷すると、再現できない異常な動作が発生することです。マシンで動作しているように見える場合でも、未定義の動作を呼び出すことは常に避ける必要があります。

于 2012-12-23T14:23:50.567 に答える
2

これは、大きなクラッシュの可能性を含む未定義の動作です。

これ以上言うことはありませんが、未定義の動作の意味については、標準からの引用が役立つ場合があります。

1.3.24 未定義の動作

この国際標準が要件を課さない動作[注: この国際標準が動作の明示的な定義を省略した場合、またはプログラムが誤った構成または誤ったデータを使用した場合、未定義の動作が予想される場合があります。許容される未定義の動作は、状況を完全に無視して予測不可能な結果を​​もたらすことから、翻訳中またはプログラム実行中に環境に特有の文書化された方法で動作すること (診断メッセージの発行の有無にかかわらず)、翻訳または実行の終了 (診断メッセージの発行を伴う) にまで及びます。診断メッセージの)。多くの誤ったプログラム構造は、未定義の動作を引き起こしません。それらは診断される必要があります。— 終了注記]

于 2012-12-23T14:24:23.057 に答える