249

delete this;delete-statement がクラスのそのインスタンスで実行される最後のステートメントである場合、許可されますか? もちろん、this-pointer によって表されるオブジェクトがnewly-created であることは確かです。

私は次のようなことを考えています:

void SomeModule::doStuff()
{
    // in the controller, "this" object of SomeModule is the "current module"
    // now, if I want to switch over to a new Module, eg:

    controller->setWorkingModule(new OtherModule());

    // since the new "OtherModule" object will take the lead, 
    // I want to get rid of this "SomeModule" object:

    delete this;
}

これはできますか?

4

10 に答える 10

256

C++ FAQ Lite には、これ専用のエントリがあります。

この引用はそれをうまくまとめていると思います

注意さえしていれば、オブジェクトが自殺しても問題ありません (これを削除してください)。

于 2010-06-30T15:47:37.287 に答える
95

はい、delete this;(あなたが指摘したように)オブジェクトが動的に割り当てられ、(もちろん)破棄された後にオブジェクトを使用しようとしないことを保証する限り、結果は定義されています。delete this;何年にもわたって、他のポインタを削除するのではなく、標準が具体的に何について述べているかについて多くの質問がなされてきました。それに対する答えはかなり短くて単純です:それは何も言いません。delete'sオペランドは、オブジェクトへのポインタまたはオブジェクトの配列を指定する式でなければならないというだけです。メモリを解放するために呼び出す割り当て解除関数(ある場合)をどのように把握するかなどについてはかなり詳細に説明しますが、delete(§[expr.delete])のセクション全体では具体的にはまったく触れていませんdelete this;。destrucorsのセクションは言及しますdelete this一箇所で(§[class.dtor] / 13):

仮想デストラクタの定義(暗黙の定義(15.8)を含む)の時点で、非配列デストラクタ関数は、式の場合、デストラクタのクラスの非仮想デストラクタに表示されるこれを削除するかのように決定されます(8.3.5を参照)。 )。

これは、標準が有効であると見なすという考えを支持する傾向がありdelete this;ます。無効である場合、そのタイプは意味がありません。delete this;私の知る限り、これが標準で言及されている唯一の場所です。

とにかく、一部の人delete thisは厄介なハックを検討し、それを聞く人には避けるべきだと言います。一般的に引用される問題の1つは、クラスのオブジェクトが動的にのみ割り当てられるようにすることの難しさです。他の人はそれを完全に合理的なイディオムと見なし、常にそれを使用します。個人的には、私は真ん中のどこかにいます。私はそれを使用することはめったにありませんが、それが仕事に適したツールであると思われる場合は、遠慮なく使用してください。

このテクニックを主に使用するのは、ほぼ完全に独自のライフを持​​つオブジェクトを使用する場合です。James Kanzeが引用した1つの例は、彼が電話会社で働いていた請求/追跡システムでした。あなたが電話をかけ始めると、何かがそれに気づき、phone_callオブジェクトを作成します。その時点から、phone_callオブジェクトは電話の詳細を処理します(ダイヤル時に接続を確立する、電話がいつ開始されたかを示すエントリをデータベースに追加する、電話会議を行う場合はさらに多くの人に接続するなど)。通話の最後の人が電話を切ると、phone_callオブジェクトは最後の簿記を行い(たとえば、電話を切ったときに言うエントリをデータベースに追加して、通話の長さを計算できるようにします)、その後、自分自身を破棄します。の寿命phone_callオブジェクトは、最初の人がいつ通話を開始し、最後の人がいつ通話を終了するかに基づいています。システムの他の部分の観点からは、基本的に完全に任意であるため、コード内の字句スコープに関連付けることはできません。 、またはその順序で何か。

この種のコーディングがどれほど信頼できるかを気にする人にとっては、ヨーロッパのほぼすべての地域との間で電話をかけると、コードによって(少なくとも部分的に)処理される可能性がかなり高くなります。それはまさにこれを行います。

于 2010-06-30T15:50:58.023 に答える
49

それがあなたを怖がらせるなら、完全に合法的なハックがあります:

void myclass::delete_me()
{
    std::unique_ptr<myclass> bye_bye(this);
}

delete thisただし、慣用的な C++だと思いますが、これは好奇心として提示するだけです。

この構成が実際に役立つ場合があります。オブジェクトからのメンバー データを必要とする例外をスローした後で、オブジェクトを削除できます。スローが行われるまで、オブジェクトは有効なままです。

void myclass::throw_error()
{
    std::unique_ptr<myclass> bye_bye(this);
    throw std::runtime_exception(this->error_msg);
}

std::auto_ptr注:の代わりに使用できる C++11 より古いコンパイラを使用している場合std::unique_ptr、同じことが行われます。

于 2010-06-30T15:56:10.043 に答える
26

C ++が設計された理由の1つは、コードを簡単に再利用できるようにすることでした。一般に、C ++は、クラスがヒープ、配列、またはスタックのいずれでインスタンス化されても機能するように作成する必要があります。「これを削除」は、ヒープ上に単一のインスタンスが定義されている場合にのみ機能するため、非常に悪いコーディング手法です。また、ヒープをクリーンアップするためにほとんどの開発者が通常使用する別のdeleteステートメントは使用しない方がよいでしょう。これを行うことは、将来、削除ステートメントを追加することによって、誤って認識されたメモリリークを修復するメンテナンスプログラマーがいないことも前提としています。

現在の計画がヒープにインスタンスを1つだけ割り当てることであることが事前にわかっている場合でも、将来、幸運な開発者がやって来て、スタックにインスタンスを作成することにした場合はどうでしょうか。または、クラスの特定の部分を切り取って、スタックで使用する予定の新しいクラスに貼り付けた場合はどうなりますか?コードが「deletethis」に達すると、コードはオフになって削除されますが、オブジェクトがスコープ外になると、デストラクタが呼び出されます。その後、デストラクタはそれを再度削除しようとします。以前は、このようなことを行うと、プログラムだけでなく、オペレーティングシステムとコンピューターを再起動する必要がありました。いずれにせよ、これは強くお勧めできません。ほとんどの場合、これは避ける必要があります。私は必死になって、真剣に漆喰を塗らなければならないでしょう、

于 2012-06-24T01:42:27.323 に答える
23

許可されています (その後はオブジェクトを使用しないでください) が、実際にはそのようなコードは書きません。orを呼び出し、次のように見えるdelete this関数にのみ表示する必要があると思います。releaseReleasevoid release() { ref--; if (ref<1) delete this; }

于 2010-06-30T16:19:23.917 に答える
17

さて、コンポーネント オブジェクト モデル (COM) では、取得したオブジェクトを解放するたびに呼び出されるメソッドdelete thisの一部として構築を行うことができます。Release

void IMyInterface::Release()
{
    --instanceCount;
    if(instanceCount == 0)
        delete this;
}
于 2010-06-30T16:30:05.700 に答える
8

これは、参照カウント オブジェクトのコア イディオムです。

参照カウントは決定論的ガベージ コレクションの強力な形式です。これにより、「スマート」ポインターなどに頼ってオブジェクトを処理するのではなく、オブジェクトが OWN ライフタイムを管理することが保証されます。基になるオブジェクトは、ポインターが実際のオブジェクトのメンバー整数 (参照カウント) をインクリメントおよびデクリメントするように設計された "参照" スマート ポインターを介してのみアクセスされます。

最後の参照がスタックから削除されるか、削除されると、参照カウントはゼロになります。オブジェクトのデフォルトの動作は、ガベージ コレクションへの「delete this」の呼び出しになります。私が作成したライブラリは、基本クラスで保護された仮想「CountIsZero」呼び出しを提供するため、キャッシュなどのためにこの動作をオーバーライドできます。

これを安全にするための鍵は、問題のオブジェクトの CONSTRUCTOR へのアクセスをユーザーに許可しない (保護する) ことですが、代わりに、静的メンバー (FACTORY など) を "static Reference CreateT(...)" のように呼び出すようにすることです。そうすれば、それらは常に通常の「new」で構築され、生のポインターが利用できないことが確実にわかるため、「これを削除」しても爆発することはありません。

于 2013-12-20T05:04:57.540 に答える
7

あなたはそうすることができます。ただし、これに割り当てることはできません。したがって、「私はビューを変更したい」と述べている理由は非常に疑わしいようです。私の意見では、より良い方法は、ビューを保持するオブジェクトがそのビューを置き換えることです。

もちろん、あなたは RAII オブジェクトを使用しているので、実際に delete を呼び出す必要はまったくありません...よね?

于 2010-06-30T16:19:08.237 に答える
4

これは古い回答済みの質問ですが、@Alexandre が「なぜこれをやりたいと思うのですか?」と尋ねたので、今日の午後に検討している使用例を提供できると思いました。

レガシーコード。ネイキッド ポインター Obj*obj を使用し、最後に delete obj を付けます。

残念ながら、オブジェクトをより長く存続させる必要がある場合がありますが、頻繁ではありません。

参照カウントのスマートポインタにすることを検討しています。しかし、どこでも使用する場合は、変更するコードがたくさんあります。ref_cnt_ptr<Obj>また、裸の Obj* と ref_cnt_ptr を混在させると、最後の ref_cnt_ptr がなくなったときに、Obj* がまだ生きている場合でも、オブジェクトを暗黙的に削除できます。

したがって、explicit_delete_ref_cnt_ptr を作成することを考えています。つまり、明示的な削除ルーチンでのみ削除が行われる参照カウント ポインターです。既存のコードがオブジェクトの有効期間を認識している場所と、オブジェクトをより長く存続させる新しいコードで使用します。

explicit_delete_ref_cnt_ptr が操作されると、参照カウントが増減します。

ただし、explicit_delete_ref_cnt_ptr デストラクタで参照カウントがゼロであると見なされた場合は解放しません。

明示的な削除のような操作で参照カウントがゼロであると見なされた場合にのみ解放します。たとえば、次のようなものです。

template<typename T> class explicit_delete_ref_cnt_ptr { 
 private: 
   T* ptr;
   int rc;
   ...
 public: 
   void delete_if_rc0() {
      if( this->ptr ) {
        this->rc--;
        if( this->rc == 0 ) {
           delete this->ptr;
        }
        this->ptr = 0;
      }
    }
 };

わかりました、そのようなもの。参照カウント ポインター型が、rc された ptr デストラクタで指されているオブジェクトを自動的に削除しないのは少し珍しいことです。しかし、これにより、ネイキッド ポインターと rc されたポインターを混在させることが少し安全になるようです。

しかし、これまでのところ、これを削除する必要はありません。

しかし、それから私は思いつきました: 指しているオブジェクトが参照カウントされていることを知っている場合、たとえば、カウントがオブジェクト内 (または他のテーブル内) にある場合、ルーチン delete_if_rc0 はのメソッドになる可能性があります(スマート) ポインターではなく、pointee オブジェクトです。

class Pointee { 
 private: 
   int rc;
   ...
 public: 
   void delete_if_rc0() {
        this->rc--;
        if( this->rc == 0 ) {
           delete this;
        }
      }
    }
 };

実際には、メンバー メソッドである必要はまったくありませんが、フリー関数である可能性があります。

map<void*,int> keepalive_map;
template<typename T>
void delete_if_rc0(T*ptr) {
        void* tptr = (void*)ptr;
        if( keepalive_map[tptr] == 1 ) {
           delete ptr;
        }
};

(ところで、コードが正しくないことはわかっています。すべての詳細を追加すると読みにくくなるため、このままにしておきます。)

于 2012-04-27T22:29:10.543 に答える
0

オブジェクトがヒープ内にある限り、削除は有効です。オブジェクトがヒープのみであることを要求する必要があります。これを行う唯一の方法は、デストラクタを保護することです。この方法では、 delete は class からのみ呼び出される可能性があるため、削除を確実にするメソッドが必要になります。

于 2016-10-04T06:24:50.470 に答える