14

デストラクタを明示的に呼び出すと(myObject。〜Object())、これにより、オブジェクトが適切に破棄されることが保証されますか(すべての子デストラクタを呼び出す)?

いくつかのコードをOK:

class Object
{
   virtual ~Object()
   {}
};

class Widget : public Object
{
   virtual ~Widget()
   {}
};

...
Object* aWidget = new Widget(); //allocate and construct
aWidget->~Object(); //destroy and DON'T deallocate

オブジェクトを削除できることはわかっていますが、削除したくありません。重要な最適化として、割り当てられたメモリを手元に置いておきたい。

ありがとう!

4

11 に答える 11

16

答えは...ほとんど常にです。

オブジェクトに非仮想デストラクタがあり、解放が必要な子要素を追加するためにサブクラス化されている場合...オブジェクト基本クラスでデストラクタを呼び出しても、子要素は解放されません。これが、デストラクタを常に仮想として宣言する必要がある理由です。

2つの共有ライブラリがオブジェクトを参照するという興味深いケースがありました。定義を変更して、解放が必要な子オブジェクトを追加しました。オブジェクト定義を含む最初の共有ライブラリを再コンパイルしました。

ただし、2番目の共有ライブラリは再コンパイルされませんでした。これは、新しく追加された仮想オブジェクト定義を認識していなかったことを意味します。Deleteは、単にfreeと呼ばれる2番目の共有ライブラリから呼び出され、仮想デストラクタチェーンを呼び出しませんでした。結果は厄介なメモリリークでした。

于 2009-06-24T01:52:19.337 に答える
10

はい。しかし、聖なる煙、あなたはこれについて確信していますか?もしそうなら、私はあなたのを構築するために配置newWidgetを使用します。配置newを使用してからデストラクタを明示的に呼び出すことは、異常な場合でも許容できるイディオムです。

編集new:最初のオブジェクトを割り当てて、後でそのメモリを再利用するのではなく、手動でメモリを割り当てることを検討してください。これにより、メモリを完全に制御できます。たとえば、ごとに個別のメモリブロックを割り当てるのではなく、一度に大きなチャンクを割り当てることができますWidget。メモリが本当にそのような希少なリソースである場合、それはかなりの節約になるでしょう。

また、おそらくもっと重要なことは、このハイブリッドの通常/配置ソリューションnewではなく、「通常」に配置を行うことです。私はそれがうまくいかないと言っているのではなく、あなたの記憶の問題に対するかなり、ああ、創造的な解決策だと言っているだけです。newnew

于 2009-06-24T01:18:42.570 に答える
6

はい、デストラクタは、明示的に呼び出された場合でも、そのサブオブジェクトを適切に破棄します。

ご存知のように、これを行うのはまれなアクションですが、十分にテストされ、文書化されたライブラリの一部として役立つ場合があります。しかし、それが有効で安全であっても、すべてのメンテナ(あなたを含む)がそれを快適に感じることは決してないので、それを文書化(およびプロファイル)します。

于 2009-06-24T01:16:11.517 に答える
6

はい、すべての子デストラクタを呼び出すため、期待どおりに機能します。

デストラクタは結局のところ単なる関数であり、オブジェクトが削除されたときに呼び出されることがあります。

したがって、このアプローチを使用する場合は、次の点に注意してください。

#include <iostream>

class A
{
public: 
    A(){};
    ~A()
    {
        std::cout << "OMG" << std::endl;
    }
};

int main()
{
    A* a = new A;
    a->~A();
    delete a;
    return 0;
}

output:
OMG
OMG 

オブジェクトで実際にdeleteが呼び出されたときに、デストラクタが2回呼び出されるため、デストラクタでポインタを削除する場合は、必ず0に設定して、2回目のデストラクタが呼び出されても何も起こらないようにします(nullの削除など)。ポインタは何もしません)。

于 2009-06-24T03:14:11.560 に答える
5

いくつかの本当の頭痛の種を避けて、ソース/シンクパターンの既存の実装のように聞こえるBoostObjectPoolを使用してください。大きなメモリチャンクを割り当て、オブジェクトに適したサイズにスライスして、(コンストラクターを呼び出した後)ユーザーに返します。オブジェクトを削除すると、デストラクタが呼び出され、オブジェクトのリンクリストに入れられて再利用されます。自動的に拡大および縮小し、オブジェクトのインスタンスがメモリ内で互いに接近する傾向があることを確認します。

他に何もないとしても、それはあなたが研究できるコンストラクターの配置の新しい明示的な使用の良い例の実装です。

于 2009-06-24T02:36:36.953 に答える
3

はい。デストラクタは、メンバーのデストラクタをLIFOの順序で呼び出し、次に基本クラスのデストラクタを呼び出します。これらのデストラクタを呼び出すのを防ぐ方法はありません*。オブジェクトスタックは確実に巻き戻されます。

初期化とファイナライズは、C ++でのメモリの割り当てと割り当て解除から正確に分離されているため、特殊なケースが発生した場合、アプリケーションプログラマーがコンパイラーに対して自分の意図を表現できる明確な構文があります。

編集:

  • abort()またはlongjmp()を呼び出すことにより、実際には、メンバーおよび基本クラスのデストラクタの実行を防ぐことができると思います。
于 2009-06-24T01:50:05.737 に答える
2

デストラクタを実行しても、破棄されるオブジェクトによって使用されているメモリは解放されません。削除演算子はそれを実行します。ただし、デストラクタは「子オブジェクト」を削除する可能性があり、通常どおりそれらのメモリが解放されることに注意してください。

これにより、メモリ割り当てとコンストラクタ/デストラクタの実行時期を制御できるため、配置の新規/削除を確認する必要があります。

少しの情報についてはここを参照してください:

http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.9
于 2009-06-24T01:18:51.540 に答える
2

STLコンテナはこれを行います。実際、STLアロケータは、オブジェクトのデストラクタを呼び出すdestroyメソッドを提供する必要があります(アロケータは、オブジェクトを保持するために使用されたメモリの割り当てを解除するための割り当て解除メソッドも提供します)。ただし、Stroustrup(C ++プログラミング言語10.4.11)からのアドバイスは

デストラクタの明示的な呼び出しは、可能な限り避ける必要があることに注意してください。時折、それらは不可欠です。...初心者は、デストラクタを明示的に呼び出す前に3回考え、経験豊富な同僚に依頼する必要があります。

于 2009-06-24T08:17:43.527 に答える
1

デストラクタを呼び出すことは問題ありません。ただし、呼び出しているタイプに注意してください。そのタイプに仮想デストラクタがない(継承されていない)場合、予期しない動作が発生する可能性があります。

また、前述のように、デストラクタはメモリを解放しませんが、そもそもそれを手動で呼び出したい理由だと思います。

さらに、私が間違っていない限り、コンストラクタを呼び出すために新しい配置を使用した場合は、デストラクタを手動で呼び出すことが唯一のオプションです。

于 2009-06-25T13:33:30.043 に答える
0

なぜそれを破壊するのですか?メモリを上書きするだけですか?リソースの解放を適切に処理するためにロジックを実行する必要がありますか?これは言葉の乱用であり、良い考えではないことを強調しておきます。

于 2009-06-24T02:18:17.303 に答える
0

特別な割り当てと割り当て解除の動作が必要なオブジェクトのnewをオーバーライドすることを検討します。結局のところ、それが目的です。通常の新しいデストラクタと明示的に呼び出すデストラクタのハイブリッドスキームは、将来の頭痛の種のように聞こえます。手始めに、メモリリーク検出戦略はすべて失敗します。

于 2009-06-24T07:50:52.910 に答える