5

私の意見では、次のコード(C ++の質問から)はUBにつながるはずですが、そうではないようです。コードは次のとおりです。

#include <iostream>
using namespace std;
class some{ public: ~some() { cout<<"some's destructor"<<endl; } };
int main() { some s; s.~some(); }

答えは次のとおりです。

some's destructor
some's destructor

私はフォームc++のFAQライトで、デストラクタを明示的に呼び出すべきではないことを学びました。デストラクタを明示的に呼び出した後、オブジェクトを削除する必要があると思います。プログラムは、終了時に自動的にデストラクタを再度呼び出します。これはUBである必要があります。しかし、g ++で試してみたところ、上記の答えと同じ結果が得られました。

クラスが単純すぎる(新規/削除が含まれない)ためですか?それとも、この場合はUBではありませんか?

4

9 に答える 9

15

デストラクタが同じオブジェクトに対して2回呼び出されるため、動作は未定義です。

  • 明示的に呼び出すと1回
  • スコープが終了し、自動変数が破棄されたとき

存続期間が終了したオブジェクトでデストラクタを呼び出すと、C++03§12.4/6に従って未定義の動作が発生します。

存続期間が終了したオブジェクトに対してデストラクタが呼び出された場合、動作は未定義です。

オブジェクトの存続期間は、そのデストラクタが§3.8/1に従って呼び出されたときに終了します。

タイプのオブジェクトの存続期間は、次の場合にT終了します。

Tが自明でないデストラクタ(12.4)を持つクラスタイプの場合、デストラクタ呼び出しが開始されるか、または

—オブジェクトが占有するストレージが再利用または解放されます。

これは、クラスに些細なデストラクタがある場合、そのようなタイプのオブジェクトの存続期間はストレージが解放されるまで終了しないため、動作が明確に定義されていることを意味します。これは、自動変数の場合、関数が終了するまで発生しません。 。もちろん、些細なことでデストラクタを明示的に呼び出す理由はわかりません。

些細なデストラクタとは何ですか?§12.4/3は言う:

デストラクタが暗黙的に宣言されたデストラクタであり、次の場合、デストラクタは簡単です。

—そのクラスのすべての直接基本クラスには、些細なデストラクタと

—クラスタイプ(またはその配列)であるそのクラスのすべての非静的データメンバーの場合、そのような各クラスには簡単なデストラクタがあります。

他の人が述べているように、未定義の動作の1つの考えられる結果は、プログラムが正しく実行され続けているように見えることです。もう1つの考えられる結果は、プログラムがクラッシュすることです。何が起こる可能性があり、いかなる保証もありません。

于 2010-07-20T15:44:50.863 に答える
6

これは未定義の動作ですが、他のUBと同様に、少なくともある程度の動作の定義では、(多かれ少なかれ)動作しているように見える可能性があります。

基本的に、デストラクタを明示的に呼び出す必要がある(または必要な)のは、placement newと組み合わせた場合のみです(つまり、placement newを使用して指定した場所にオブジェクトを作成し、明示的なdtor呼び出しを使用してそのオブジェクトを破棄します)。

于 2010-07-20T15:22:27.190 に答える
3

http://www.devx.com/tips/Tip/12684から

未定義の動作は、プログラムが特定の状態に達したときに実装が予期しない動作をする可能性があることを示します。これは、ほとんど例外なく、バグの結果です。未定義の動作は、実行時のクラッシュ、不安定で信頼性の低いプログラム状態として現れる可能性があります。または、まれに、気付かれずに通過することもあります。

あなたの場合、デストラクタはフィールドを操作しないため、クラッシュしません。実際、クラスにはデータメンバーがまったくありません。それが行われ、デストラクタの本体で何らかの方法で操作した場合、2回目のデストラクタの呼び出し中に実行時例外が発生する可能性があります。

于 2010-07-20T15:29:52.333 に答える
1

ここでの問題は、削除/割り当て解除とデストラクタが別個の独立した構造であるということです。new/割り当てとコンストラクターによく似ています。上記の一方だけを実行でき、もう一方は実行できません。

一般的なケースでは、このシナリオは有用性に欠けており、スタックに割り当てられた値と混同されるだけです。私の頭のてっぺんから、あなたがこれをしたいと思う良いシナリオを考えることはできません(私は潜在的に1つあると確信していますが)。ただし、これが合法であるという不自然なシナリオを考えることは可能です。

class StackPointer<T> {
  T* m_pData;
public:
  StackPointer(T* pData) :m_pData(pData) {}
  ~StackPointer() { 
    delete m_pData; 
    m_pData = NULL; 
  }
  StackPointer& operator=(T* pOther) {
    this->~StackPointer();
    m_pData = pOther;
    return this;
  }
};

注:この方法でクラスをコーディングしないでください。代わりに、明示的なReleaseメソッドを使用してください。

于 2010-07-20T15:21:15.080 に答える
1

デストラクタはクラスメンバー変数を参照しないため、おそらく正常に機能します。デストラクタ内で変数を試した場合delete、2回目に自動的に呼び出されたときに問題が発生する可能性があります。

それではまた、未定義の振る舞いで、誰が知っていますか?:)

于 2010-07-20T15:22:17.303 に答える
0

期待する未定義の動作を定義できますか?未定義はランダム(または壊滅的)を意味するものではありません。特定のプログラムの動作は呼び出し間で繰り返される可能性があります。未定義であり、何が起こるかは保証されていないため、特定の動作に依存できないことを意味します。

于 2010-07-20T15:45:59.227 に答える
0

main関数が行うことは、スタック上のスペースを予約し、someのコンストラクターを呼び出し、最後にsomeのデストラクタを呼び出すことです。これは、関数内にどのようなコードを入れても、常にローカル変数で発生します。コンパイラは、デストラクタを手動で呼び出したことを検出しません。

とにかく、placement-newで作成されたオブジェクトを除いて、オブジェクトのデストラクタを手動で呼び出さないでください。

于 2010-07-20T15:24:22.220 に答える
0

コードをOKにしたい場合は、placement newを呼び出して、終了する前に入力し直すだけでよいと思います。デストラクタへの呼び出しは問題ではありません。スコープを離れたときに行われるデストラクタへの2回目の呼び出しです。

于 2010-07-20T15:42:24.630 に答える
0

それは未定義の振る舞いです。未定義の動作はダブルデストラクタ呼び出しであり、デストラクタ呼び出し自体ではありません。例を次のように変更した場合:

#include <iostream>
using namespace std;
class some{ public: ~some() { [INSERT ANY CODE HERE] } };
int main() { some s; s.~some(); }

ここで、[ここに任意のコードを挿入]は任意のコードに置き換えることができます。結果には予測できない副作用があり、それが未定義と見なされる理由です。

于 2010-07-20T17:13:01.417 に答える