web-dev-qa-db-ja.com

これは削除できますか?

Delete-statementがクラスのそのインスタンスで実行される最後のステートメントである場合、delete this;は許可されますか?もちろん、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;
}

これはできますか?

217

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

私はこの引用がうまくまとめていると思います

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

225
JaredPar

はい、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つは、クラスのオブジェクトが動的にのみ割り当てられることを保証することが難しいことです。他の人はそれを完全に合理的なイディオムと見なし、常に使用しています。個人的には、私は真ん中のどこかにいます。私はめったにそれを使用しませんが、それが仕事に適切なツールであると思われる場合は、heしないでください。

このテクニックを使用する主な時間は、ほぼ完全に独自の寿命を持つオブジェクトを使用する場合です。 James Kanzeが引用した例の1つは、電話会社で働いていた請求/追跡システムでした。電話をかけると、何かがそれに気づき、phone_callオブジェクトを作成します。その時点以降、phone_callオブジェクトは電話の詳細を処理します(ダイヤル時に接続を確立し、データベースにエントリを追加して、電話が開始したことを通知します。など)通話中の最後の人が電話を切ると、phone_callオブジェクトは最終的なブックキーピングを行います(たとえば、電話を切った時間を知らせるためにデータベースにエントリを追加します。呼び出しがあった)、それからそれ自体を破壊します。 phone_callオブジェクトの存続期間は、最初の人が通話を開始し、最後の人が通話を終了するタイミングに基づいています。システムの他の観点から見ると、基本的に完全に任意なので、できませんコード内のレキシカルスコープ、またはその順序の何かに結び付けます。

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

80
Jerry Coffin

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

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_ptrの代わりにstd::unique_ptrを使用できますが、同じことを行います。

45
Mark Ransom

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

現在の計画がヒープ上に単一のインスタンスのみを割り当てることであることを事前に知っていても、幸運な開発者が将来来てスタック上にインスタンスを作成することにした場合はどうなりますか?または、クラスの特定の部分を切り取って、スタックで使用する予定の新しいクラスに貼り付けるとどうなりますか?コードが「これを削除」に達すると、コードは消えて削除されますが、オブジェクトが範囲外になると、デストラクタが呼び出されます。デストラクタはそれを再度削除しようとしますが、その後は停止します。過去には、このようなことを行うと、プログラムだけでなく、オペレーティングシステムとコンピューターの再起動が必要になります。いずれにせよ、これは強く推奨されておらず、ほとんどの場合回避する必要があります。私はこれを行うコードを書くために働いていた会社を必死に、真剣に塗りつぶさなければならないか、本当に嫌いになります。

22
Bob Bryan

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

19

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

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

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

参照カウントは、確定的ガベージコレクションの強力な形式です。オブジェクトを「スマート」ポインターなどに依存するのではなく、オブジェクトが自身のライフタイムを管理するようにします。基になるオブジェクトは、「参照」スマートポインターを介してのみアクセスされ、ポインターが実際のオブジェクトのメンバー整数(参照カウント)を増減するように設計されています。

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

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

8
Zack Yezek

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

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

7
Edward Strange

これは古くて答えられた質問ですが、@ Alexandreは「なぜ誰もこれをしたいのですか?」と尋ね、今日の午後に考えている使用例を提供できると思いました。

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

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

参照カウントスマートポインターにすることを検討しています。しかし、ref_cnt_ptr<Obj>をすべての場所で使用する場合、変更するコードはlotsになります。裸の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されたptrデストラクタでポイントされたオブジェクトを自動的に削除しないことは少し珍しいです。しかし、これにより、裸のポインタとrcされたポインタの混合が少し安全になるかもしれません。

ただし、これを削除する必要はありません。

しかし、それは私に起こりました:オブジェクトが指している場合、指示先は、参照カウントされていることを知っています、例えばカウントがオブジェクト内(または他のテーブル内)にある場合、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;
        }
};

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

4
Krazy Glew

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

0