3

古いコードを少し C から C++ に移植しています。古いコードはオブジェクトのようなセマンティクスを使用し、ある時点でオブジェクトの破棄と現在使用されていないメモリの解放を分離し、その間に次のようなことが起こります。

Object_Destructor(Object *me) { free(me->member1), free(me->member2) }

ObjectManager_FreeObject(ObjectManager *me, Object *obj) { free(obj) }

上記の機能は、標準デストラクタ ( ~Object) とそれに続く への呼び出しを使用して、C++ で可能delete objですか? それとも、私が恐れているように、それを行うとデストラクタが 2 回呼び出されるのでしょうか?

特定のケースでは、operator deleteofObjectもオーバーライドされます。私が他の場所で読んだ定義(「演算子deleteが使用され、オブジェクトにデストラクタがある場合、デストラクタは常に呼び出されます)は、オーバーライドされた演算子の場合に正しいですか?

4

9 に答える 9

6

delete operatorメモリを解放するために使用され、デストラクタが呼び出されたかどうかに関係なく変更されません。最初にデストラクタが呼び出され、その後のみdelete operator、メモリの割り当てを解除するために使用されます。

つまり、C++ のデストラクタと削除演算子では、目的のセマンティクスを実現することはできません。

サンプル:

#include <iostream>
#include <new>
using namespace std;

struct foo {
    ~foo() { cout << "destructor\n"; }
    void operator delete(void* p) { 
        cout << "operator delete (not explicitly calling destructor)\n"; 
        free(p);
        cout << "After delete\n"; 
    }
};

int main()
{
    void *pv = malloc(sizeof(foo));
    foo* pf = new (pv) foo; // use placement new
    delete pf;
}

出力:

デストラクタ

operator delete (明示的にデストラクタを呼び出さない)

削除後

于 2009-11-16T15:04:52.440 に答える
3

オーバーロードdeleteは、placement delete とは対照的に、実行を開始する前にデストラクタを暗黙的に呼び出します (ただし、placement delete は直接呼び出されることは想定されていません)。したがって、オブジェクトを「削除」する場合は、事前に破棄しないでください。デストラクタが 2 回呼び出されます。ただし、オブジェクトが新しい配置で作成された場合は、明示的な破棄が必要です (ただし、その場合は、を使用してオブジェクトを破棄しませんdelete)

于 2009-11-16T15:27:06.310 に答える
2

なぜそれが不可能だと人々が言うのか、私には絶対にわかりません。

初期化を構築から切り離し、ゼロ化(tm)を破棄から切り離すのは実際には非常に簡単です。

class Clike
{
public:
  Clike() : m_usable(true) {}

  void clear(); // performs clean up then sets m_usable to false
  ~Clike() { if (m_usable) this->clear(); }

private:
  bool m_usable;
  // real variables
};

次に、次のように使用できます。

Clike* c = new Clike();

c->clear(); // performs cleanup

// stuff

delete c;

実際、デストラクタは決してスローして何も返さないようにする必要があるため、操作でエラーが報告されるようにcleanupdestructionとを分離することはまったく珍しいことではありません。cleanup特にDB接続などの複雑な獣の場合...

これは「デストラクタ」ではありませんが、確実に機能するため、提示されたCコードは、新しい配置などを行わなくても、実際には完全に再現可能です。

于 2009-11-17T10:08:53.500 に答える
2

オブジェクトが破壊されてからオブジェクトのメモリが解放されるまでの間に、どのようなことが起こりますか? オブジェクトと関係がない場合は、デストラクタが表示されるオブジェクトを削除できるはずです。もしそうなら、それは悪い考えのように聞こえるので、私はそれを非常に注意深く調べます.

セマンティクスを再現する必要がある場合は、すべてのリソースを解放するメンバー関数を用意し、destruct 関数の代わりにそれを使用します。関数を安全に複数回呼び出すことができることを確認し、念のため C++ デストラクタに含めます。

于 2009-11-16T15:33:35.983 に答える
1

破壊と削除を分離することはできますが、実際にはそうしたくないでしょう。

new char[]またはでメモリを割り当ててから、mallocplacement new を呼び出すと、削除 (または ) から破棄 (デストラクタを直接呼び出すことによって行う) を分離できますfree。しかし、クラスのオーバーロードされた を呼び出すのではなく、char 配列 (または)operator deleteを呼び出しています。delete[]free

クラス (operator delete をオーバーロードしたクラス) へのポインタを介して delete を呼び出すと、そのクラスのデストラクタが呼び出されます。したがって、デストラクタなしで delete を呼び出すという意味でそれらを分離する方法はありません。

于 2009-11-16T15:28:28.303 に答える
1

いいえ、できません。

deleteデストラクタを呼び出します。

スタッフが正しい順序で発生するようにするには、何らかのロジックを作成する必要があります。

于 2009-11-16T15:29:48.630 に答える
0

実装の std::allocators を見てください。答えは「はい、分離されている可能性があります」です。これは非常に簡単に実行できますが、あまり頻繁には見られません。

于 2009-11-16T15:31:34.147 に答える
-1

デストラクタは削除に結合されており、デストラクタを明示的に呼び出すことができないため、2 回呼び出すことはできません (または、少なくとも非常にまれで珍しいものであり、私は見たことがありません)。

ただし、 object_destructor() をメンバー関数にして明示的に呼び出すだけです (通常、2 回呼び出されても安全にするのは良いスタイルです。ただし、NULL ポインターで free() を呼び出すと、合法であるため、object_destructor の代替バージョンは、それがどのように行われるかを強調するためのものです。

CLASS A {
   object_destructor() { free(this->member1); free(this->member2); }

   alternate_version_of_object_destructor() {  
           if (this->member1) { free(this->member1); this->member1= NULL; } 
           if (this->member2) { free(this->member2); this->member2= NULL; } }

    ~A() { /* do nothing or just call this->object_destructor() for safety */ }
}


foo() {
    A *pa= new A;


    pa->object_destructor();  /* member cleanup */
    delete pa;                /* free object memory */
} 
于 2009-11-17T11:43:35.233 に答える
-1

プレースメント newが必要なようです。一方で、コードがかなり毛むくじゃらになっているようにも聞こえます。重いリファクタリングの時期かもしれません。

于 2009-11-16T15:17:03.320 に答える