web-dev-qa-db-ja.com

デストラクタへの明示的な呼び出し

私は次のコードスニペットを見つけました。

#include <iostream>
#include <string>
using namespace std;
class First
{
    string *s;
    public:
    First() { s = new string("Text");}
    ~First() { delete s;}
    void Print(){ cout<<*s;}
};

int main()
{
    First FirstObject;
    FirstObject.Print();
    FirstObject.~First();
}

テキストは、このスニペットがランタイムエラーを引き起こすはずであると述べました。今、私はそれについて本当に確信が持てなかったので、私はそれをコンパイルして実行しようとしました。出来た。奇妙なのは、関係するデータが単純であるにもかかわらず、「テキスト」を印刷した後、1秒後にプログラムが途切れることです。

そのようなデストラクタを明示的に呼び出すことが合法かどうかわからないため、デストラクタに出力される文字列を追加しました。プログラムは文字列を2回印刷しました。そのため、通常のプログラム終了は明示的な呼び出しを認識せず、オブジェクトを再度破棄しようとするため、デストラクタが2回呼び出されると推測しました。

単純な検索により、自動化されたオブジェクトでデストラクタを明示的に呼び出すことは危険であることが確認されました。だから、私は自分のコンパイラ(VS 2017)またはこの特定のプログラムで幸運でした。

テキストは単に実行時エラーについて間違っていますか?または、ランタイムエラーが発生することは本当に一般的ですか?それとも、私のコンパイラは、この種のことに対するある種の監視メカニズムを実装したのでしょうか?

21
Andrea Bocco

単純な検索により、自動化されたオブジェクトでデストラクタを明示的に呼び出すことは危険であることが確認されました。

それは本当です。自動ストレージを使用してオブジェクトを明示的に破棄すると、未定義の動作が呼び出されます。 詳細

だから、私は自分のコンパイラ(VS 2017)またはこの特定のプログラムで幸運でした。

あなたは不運だったと思います。 UBで発生する可能性のある最高の(コーダー)は、初回実行時のクラッシュです。正常に動作しているように見える場合、2038年1月19日に実稼働中にクラッシュが発生する可能性があります。

テキストは単に実行時エラーについて間違っていますか?または、ランタイムエラーが発生することは本当に一般的ですか?それとも、私のコンパイラは、この種のことに対するある種の監視メカニズムを実装したのでしょうか?

はい、テキストはちょっと間違っています。 未定義の動作は未定義です。実行時エラーは、多くの可能性のうちの1つにすぎません(鼻の悪魔を含む)。

未定義の動作についての良い読み物: 未定義の動作とは?

34
YSC

いいえ、これは単純なC++標準ドラフトの未定義の動作です [class.dtor] p16

オブジェクトに対してデストラクタが呼び出されると、オブジェクトは存在しなくなります。ライフタイムが終了したオブジェクト([basic.life])に対してデストラクタが呼び出された場合の動作は未定義です。 [例:自動オブジェクトのデストラクタが明示的に呼び出され、ブロックが通常オブジェクトの暗黙的な破壊を呼び出す方法で残された場合、動作は未定義です。 —例の終了

未定義の動作の定義 から見ることができます:

このドキュメントが要件を課さない動作

結果について期待することはできません。特定のマシンの特定のオプションを使用して、特定のコンパイラで作成者がそのように動作した可能性がありますが、移植可能で信頼できる結果になるとは期待できません。 実装は特定の結果を取得しようとする の場合もありますが、それは受け入れられる未定義の動作の単なる別の形式です。

さらに、 [class.dtor] p15 は、上記で引用した規範的なセクションに関するより多くのコンテキストを提供します。

[注:デストラクタの明示的な呼び出しはほとんど必要ありません。このような呼び出しの1つの用途は、配置new-expressionを使用して特定のアドレスに配置されたオブジェクトです。オブジェクトの明示的な配置と破棄のこのような使用は、専用のハードウェアリソースに対処し、メモリ管理機能を記述するために必要になる場合があります。例えば、

void* operator new(std::size_t, void* p) { return p; }
struct X {
  X(int);
  ~X();
};
void f(X* p);

void g() {                      // rare, specialized use:
  char* buf = new char[sizeof(X)];
  X* p = new(buf) X(222);       // use buf[] and initialize
  f(p);
  p->X::~X();                   // cleanup
}

—注を終了]

15
Shafik Yaghmour

テキストは単に実行時エラーについて間違っていますか?

違います。

または、ランタイムエラーが発生することは本当に一般的ですか?それとも、私のコンパイラは、この種のことに対して何らかの種類の監視メカニズムを実装しましたか?

あなたは知ることができません、そしてこれはあなたのコードが ndefined Behavior ;を呼び出したときに起こることです。実行するとどうなるかわかりません。

あなたの場合、あなたは(不)幸運でした* それはうまくいきましたが、私にとっては error (double free)を引き起こしました。


*エラーを受け取った場合、デバッグを開始します。そうしないと、たとえば大規模なプロジェクトで見逃してしまう可能性があります...

9
gsamaras