これは削除できますか?


232

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;
}

できますか?


13
主な問題はdelete this、クラスとそのクラスのオブジェクトの作成に使用される割り当てメソッドとの間に密結合を作成した場合です。OOPの最も基本的なことは、呼び出し元が何をしているかを知らないか気にしない自律クラスを作成することであるため、これは非常に悪いOO設計です。したがって、適切に設計されたクラスは、それがどのように割り当てられたかを認識したり、気にする必要はありません。なんらかの理由でこのような特殊なメカニズムが必要な場合は、実際のクラスの周りにラッパークラスを使用して、ラッパーに割り当てを処理させる方が良いと思います。
ランディン、2015年

で削除できませんsetWorkingModuleか?
ジミーT.

回答:


238

C ++ FAQ Liteには、このための特別なエントリがあります

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

注意している限り、オブジェクトが自殺する(これを削除する)ことは問題ありません。


15
:対応するFQAもいくつかの有用なコメントがあるyosefk.com/c++fqa/heap.html#fqa-16.15
アレクサンドル・C.

1
安全のために、元のオブジェクトにプライベートデストラクタを使用して、スタック上に構築されていないこと、または配列やベクトルの一部として構築されていないことを確認してください。
Cem Kalyoncu

「慎重」の定義
CinCout

3
「注意深い」は、リンクされたFAQ記事で定義されています。(FQAリンクは大抵は怒鳴る-その中のほとんどすべてのように-C ++がいかに悪いか)
CharonX

88

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

仮想デストラクタの定義(暗黙の定義(15.8)を含む)の時点で、非配列割り当て解除関数は、デストラクタのクラスの非仮想デストラクタに現れる式deleteのように決定されます(8.3.5を参照) )。

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

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

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

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


2
ありがとう、私はそれを私の記憶のどこかに置きます。コンストラクタとデストラクタをプライベートとして定義し、静的なファクトリメソッドを使用してそのようなオブジェクトを作成するとします。
Alexandre C.

@Alexandre:ほとんどの場合、とにかくそうするだろう-私が彼が取り組んでいたシステムのすべての詳細に近いところはどこにもわからないので、私はそれについて確実には言えない。
ジェリーコフィン

メモリの割り当て方法の問題を回避する方法の1つはbool selfDelete、メンバー変数に割り当てられるパラメーターをコンストラクターに含めることです。確かに、これは縄を縛るのに十分なロープをプログラマに渡すことを意味しますが、メモリリークよりも好ましいと思います。
MBraedley 2016

1
@MBraedley:私は同じことをしましたが、私にはクラッジのように見えるものを避けたいです。
Jerry Coffin

気にかける人のために...正確に動作するコードによって(少なくとも部分的に)処理されている可能性はかなり高いthisです。はい、コードは正確に処理されますthis。;)
Galaxy

46

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

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);
}

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


これをc ++ 11を使用してコンパイルすることはできませんが、特別なコンパイラオプションはありますか?また、thisポインタを移動する必要はありませんか?
フクロウ

@フクロウはあなたが何を意味するのかわからない、それは私のために働きます:ideone.com/aavQUKのものunique_ptrからを作成するには移動が必要ですが、生のポインタからは必要ありません。C ++ 17で変更がない限り? unique_ptr
Mark Ransom

ああC ++ 14、それが理由です。開発ボックスでc ++を更新する必要があります。今夜、最近登場したgentooシステムでもう一度トライします!
フクロウ

25

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

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


7
+1。なぜ反対票が投じられたのか理解できません。「クラスがヒープ上、配列内、またはスタック上でインスタンス化されるかどうかに関係なく機能するようにC ++を記述する必要があります」は非常に良いアドバイスです。
ジョー

1
あなたはちょうどあなたがオブジェクトを削除特別なクラスで自分自身を削除するオブジェクトをラップして、自分自身を、そしてスタック割り当てを防止するため、この技術を使用することができます。stackoverflow.com/questions/124880/...本当に存在しない場合があります。実行可能な代替。この手法を使用して、DLL関数によって開始されたスレッドを自己削除しましたが、DLL関数はスレッドが終了する前に戻る必要があります。
Felix Dombek 2013

コードをコピーして貼り付けるだけでコードが悪用されるようなプログラミングはできません
Jimmy T.

22

許可されています(その後はオブジェクトを使用しないでください)が、実際にはそのようなコードは記述しません。これは、次のようにdelete this呼び出された、releaseまたはRelease次のような関数でのみ表示されるはずですvoid release() { ref--; if (ref<1) delete this; }


これは、私のすべてのプロジェクトで1回だけです... :-)
cmaster-モニカを2015年

15

まあ、コンポーネントオブジェクトモデル(COM)では、必要なオブジェクトを解放するたびに呼び出されるメソッドのdelete this一部を構築できReleaseます。

void IMyInterface::Release()
{
    --instanceCount;
    if(instanceCount == 0)
        delete this;
}

8

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

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

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

これを安全にするための鍵は、ユーザーに問題のオブジェクトのコンストラクタへのアクセスを許可しない(オブジェクトを保護する)ことではなく、「静的参照CreateT(...)」のようないくつかの静的メンバー-FACTORY-を呼び出すようにすることです。このようにして、常に通常の「新しい」でビルドされ、生のポインターが使用できないことを知っているので、「これを削除する」ことで爆発することはありません。


(シングルトン)クラス「アロケーター/ガベージコレクター」、つまりすべての割り当てが行われるインターフェイスを使用して、割り当てられたオブジェクトのすべての参照カウントをそのクラスで処理できないのはなぜですか。オブジェクト自体がガベージコレクションタスクに煩わされるのではなく、指定された目的とはまったく無関係なもの。
ランディン

1
デストラクタを保護して、オブジェクトの静的およびスタック割り当てを禁止することもできます。
Game_Overture

7

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

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


4

これは古く、答えられた質問ですが、@ Alexandreは「なぜ誰もがこれをしたいのですか?」と尋ねました。

レガシーコード。裸のポインターObj * objを使用し、最後に削除objを指定します。

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

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

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

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;
      }
    }
 };

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

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

しかし、それが私に起こりました:オブジェクトが指すオブジェクト(参照先)が参照カウントされていることを知っている場合、たとえば、カウントがオブジェクト(または他のテーブル)内にある場合、ルーチンdelete_if_rc0は、 (スマート)ポインタではなく、指示先オブジェクト。

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;
        }
};

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


0

オブジェクトがヒープ内にある限り、これを削除しても問題ありません。オブジェクトがヒープのみであることを要求する必要があります。これを行う唯一の方法は、デストラクタを保護することです。この方法では、クラスからのみ削除を呼び出すことができるため、削除を確実にするメソッドが必要です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.