15

この質問は、「デストラクタを使用する必要があるのはいつ/なぜvirtualですか? 」とは異なります。'。

struct B {
  virtual void foo ();
  ~B() {}  // <--- not virtual
};
struct D : B {
  virtual void foo ();
  ~D() {}
};
B *p = new D;
delete p;  // D::~D() is not called

質問

  1. これは未定義の動作として分類できますか(確実~D()に呼び出されることはないことを認識しています)?
  2. ~D()空の場合はどうなりますか。それはコードに何らかの影響を及ぼしますか?
  3. new[]/ delete[]withを使用すると、デストラクタの性質に関係なくB* p;~D()は確実に呼び出されません。virtualそれは未定義の動作ですか、それとも明確に定義された動作ですか?
4

4 に答える 4

19

いつ/なぜ仮想デストラクタを使用する必要がありますか?
ハーブサッターのガイドラインに従ってください:

基本クラスのデストラクタは、パブリックと仮想、または保護された非仮想のいずれかである必要があります

これは未定義動作として分類できますか(〜D()が確実に呼び出されることはないことを認識しています)?

これは標準による未定義動作であり、通常、派生クラスのデストラクタが呼び出されず、メモリリークが発生しますが、標準はこの点に関して何も保証しないため、未定義動作の影響後を推測することは関係ありません。 。

C ++ 03標準:5.3.5削除

5.3.5 / 1:

式の削除演算子は、new-expressionによって作成された最も派生したオブジェクト(1.8)または配列を破棄します。
削除式
:::opt削除キャスト式
::opt削除[]キャスト式

5.3.5 / 3:

最初の選択肢(オブジェクトの削除)では、オペランドの静的型が動的型と異なる場合、静的型はオペランドの動的型の基本クラスであり、静的型は仮想デストラクタを持っているか、動作が未定義です。 。2番目の選択肢(配列の削除)では、削除するオブジェクトの動的タイプが静的タイプと異なる場合、動作は未定義です73)。

~D()空の場合はどうなりますか。それはコードに何らかの影響を及ぼしますか?
それでも、標準では未定義動作です。派生クラスのデストラクタが空の場合、プログラムは正常に動作する可能性がありますが、これも特定の実装の実装定義の側面であり、技術的には未定義動作です。

ここでは、派生クラスのデストラクタを仮想化しないと、派生クラスのデストラクタが呼び出されないという保証はなく、この仮定は正しくないことに注意してください。標準に従って、未定義動作の土地でクロスオーバーすると、すべての賭けはオフになります。

彼の標準が未定義動作について言っていることに注意してください。

C ++ 03標準:1.3.12未定義動作[defns.undefined]

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

派生したデストラクタのみが呼び出されない場合は、上記の引用の太字のテキストが適用されます。これは、実装ごとに明らかに開いたままになっています。

于 2011-12-22T04:10:49.953 に答える
7
  1. 未定義動作
  2. 最初の注意点として、これらのデコンストラクターは通常、思ったほど空ではありません。すべてのメンバーを分解する必要があります)デコンストラクターが本当に空(POD?)であっても、コンパイラーによって異なります。規格では定義されていません。すべての標準的なケアのために、あなたのコンピュータは削除で爆発する可能性があります。
  3. 未定義動作

継承されることを意図したクラスに非仮想パブリックデストラクタが存在する理由は実際にはありません。この記事、ガイドライン#4を見てください。

保護された非仮想デストラクタとshared_ptrs(静的リンクがあります)、またはパブリック仮想デストラクタのいずれかを使用します。

于 2011-12-22T03:53:35.977 に答える
2

他の人が再確認したように、ベースのデストラクタは仮想ではなく、誰もステートメントを作成できないため、これは完全に定義されていません。標準への参照と詳細については、このスレッドを参照してください

(もちろん、個々のコンパイラーは特定の約束をする権利がありますが、この場合、私はそれについて何も聞いていません。)

でも面白いと思いますが、この場合、mallocfreeはとよりも定義されている場合がnewありdeleteます。おそらく、代わりにそれらを使用する必要があります:-)

基本クラスと派生クラスがあり、どちらにも仮想メソッドがない場合、次のように定義されます。

Base * ptr = (Base*) malloc(sizeof(Derived)); // No virtual methods anywhere
free(ptr); // well-defined

Dに複雑な追加メンバーがある場合、メモリリークが発生する可能性がありますが、これとは別に、動作が定義されています。

于 2011-12-22T04:07:53.567 に答える
0

(私は他の答えを削除するかもしれないと思います。)

その振る舞いについてのすべては未定義です。より明確に定義された動作が必要な場合は、自分で調べたりshared_ptr、似たようなものを実装したりする必要があります。以下は、仮想性に関係なく、定義された動作です。

    shared_ptr<B> p(new D);
    p.reset(); // To release the object (calling delete), as it's the last pointer.

shared_ptrの主なトリックは、テンプレート化されたコンストラクターです。

于 2011-12-22T04:49:08.510 に答える