4

私はオーバーロード演算子deleteを試しています。これにより、スマートポインターを操作したくないユーザーにプレーンポインターを返すことができ、オブジェクトが削除されるタイミングを制御できます。

私は、いくつかの魂で構築され、何もしないオーバーロードされた演算子deleteと、魂の数を減らす(そして自慢する)デストラクタを持つクラスCatを定義します。魂が0に達すると、デストラクタはグローバル:: deleteを呼び出し、猫は死にます。

これは非常に単純に聞こえますが、期待どおりに機能しません。コードは次のとおりです。

class Cat {
public:
    Cat(string n): name(n), souls(9)
    { cout << "Myaou... " << name << " is born\n"; }

    ~Cat();
    void operator delete(void *p) { cout << "!!! operator delete called\n"; }
    void report()
    { cout << name << "'s here, " << souls << " souls to spend\n"; }

    friend ostream& operator<< (const ostream& o, const Cat& cat);
private:
    void kill();
    const string name;
    int souls;
};

Cat::~Cat()
{
    cout << "!!! dtor called\n";
    kill();
}

void Cat::kill()
{
    if (--souls)
        cout << name << " is still alive! I have " << souls << " souls left.\n";
    else {
        cout << name << " is dying... good bye world!\n";
        ::delete((void*)this);
    }
}

ostream& operator<< (const ostream& o, const Cat& cat)
{
    return o << cat.name << "'s here, " << cat.souls << " souls to spend\n";
}

これがメインです:

int main()
{
    Cat *p = new Cat("Mitzi");

    for (;;)
    {
        char c[100];
//      cout << *p;
        p->report();
        cout << "come on, hit me!";
        cin >> c;
        delete p;
    }
}

ループが9回実行された後、何か不快な(クラッシュ)が発生することを期待します。ただし、これは出力です。

Myaou... Mitzi is born
Mitzi's here, 9 souls to spend
come on, hit me!c
!!! dtor called
Mitzi is still alive! I have 8 souls left.
!!! operator delete called
's here, 8 souls to spend
come on, hit me!c
!!! dtor called
 is still alive! I have 7 souls left.
*** glibc detected *** /home/davidk/workspace/string_test/Debug/string_test: double free or corruption (fasttop): 0x080cd008 ***

最初の削除後に名前メンバーが破棄され、次の削除によってクラッシュが発生したようです。説明はありますか?Linuxでgccを使用してコンパイルしていますが、コンパイラのバグである可能性がありますか?

ところで、repotr()の代わりにcout << *pのようにoperator<<()を使用すると、それも奇妙でした。演算子<<()内からコンストラクターを呼び出す無限ループに入りました。何が起きてる?:)

ありがとう!

4

5 に答える 5

20

考えられるすべてのC++コンセプトを誤用したため、エラーが発生します。がdelete p;初めて呼び出されると、C++はエンティティの内部Cat::~Cat()を暗黙的に破棄するデストラクタを呼び出します。std::stringdelete p;2回呼び出されると、のCat::~Cat()デストラクタが再実行されます。これにより、バッファへのポインタが無効にならないためstd::string、プログラムがクラッシュする未定義の動作が発生します。これに対処すると、有名なダブルフリーになります。std::string::~string()std::string::~string()

于 2009-12-21T11:31:05.000 に答える
7

オペレーターdeleteがオブジェクトデストラクタを呼び出し、その後、あなたは無人地帯にいます。他の人が指摘したように、あなたがやろうとしていることは不可能です。

また、オブジェクトがヒープ上とスタック上に構築されている場合、動作に一貫性がないという点で、あなたがしていることは少し危険です。

演算子をオーバーライドするというアイデアでdeleteは、newを使用して構築された場合、オブジェクトはいくつかの内部ロジック(ライフの数が9に達した)に応じて存続します。

スタック(Cat cat("Chesire cat");)上に構築されると、オブジェクトはスコープ外になると常に破壊されます。あなたがやろうとしていることを達成するために、あなたはまた、「破壊をやめる」ためにデストラクタの振る舞いを変える必要があるでしょう。これは、非常に正当な理由からも不可能です。

したがって、参照カウントが必要な場合は、独自のメカニズムを実装するだけです。結局のところ、自分自身の参照カウントメモリ管理を少なくとも1回行っていない場合は、自分自身をC++プログラマーと呼ぶことはできません:))

于 2009-12-21T11:46:29.967 に答える
2

オブジェクトのデストラクタが呼び出されると、そのオブジェクトで行うことはすべて未定義になります。あなたがしようとしていることは不可能です。

于 2009-12-21T11:30:28.277 に答える
1

デストラクタはメモリを解放する責任を負わず、これが発生するのを防いでいません。

最初の呼び出しはメモリを解放し、2番目の呼び出しは強打します。

于 2009-12-21T11:29:53.520 に答える
1

1つのアドレスに対して複数回削除を実行することはできないことを知っておく必要があります。

于 2009-12-21T11:32:13.657 に答える