delete this; (自オブジェクトの開放)

[戻る]
はち 2003/11/10(月) 07:43:40
'はち'と申します。よろしくおねがいします。

//-------------------------------------------------------------
#include <iostream>
struct test {
    ~test() { std::cout << "デストラクタ" << std::endl; }
    void Delete() {
        delete this;
        std::cout << "このメッセージは表示されます" << std::endl;
    }
};

int main () {
    test* t = new test;
    t->Delete();
    return 0;
}
-------[実行結果]-----------
デストラクタ
このメッセージは表示されます
----------------------------

私はこのプログラムはとても危険に見える(delete this;の部分)のですが、
このようなコーディングは許されるのでしょうか?

VC++6.0 sp5 SDK Win2k

tetrapod 2003/11/10(月) 08:11:49
> 私はこのプログラムはとても危険に見える(delete this;の部分)のですが、
> このようなコーディングは許されるのでしょうか?

許されるか否か、という一点に関して言えば、許されます。
危険かどうか、という一点に関して言えば、そう、危険です。
自動変数としてオブジェクトを作るとバグってしまいますから。

私自身は delete this なる設計はしません。

kazuma 2003/11/10(月) 09:06:20
参照カウンタつきのオブジェクト。(誰からも参照されなくなると自身を delete します)
MFC で,Window が破棄されたときに,関連付けられた CWnd 派生クラスのオブジェクトを自
動的に削除する。

など便利なこともあります。

はち 2003/11/10(月) 12:38:32
tetrapodさん kazumaさん ありがとうございます。

>> 私はこのプログラムはとても危険に見える(delete this;の部分)のですが、
>> このようなコーディングは許されるのでしょうか?
>
>許されるか否か、という一点に関して言えば、許されます。
>危険かどうか、という一点に関して言えば、そう、危険です。
>自動変数としてオブジェクトを作るとバグってしまいますから。
>
>私自身は delete this なる設計はしません。

つまり、delete this は許されているが、すべきではない。
理由:
 auto変数としてのオブジェクトがdelete thisの可能性のあるコードを持つべきではな
い。

疑問2 さらに突っ込んで質問です。
    delete this 後のインスタンスはどこまで保障されているのか?

//-------------------------------------------------------------
#include <iostream>
struct test {
    int dummy;

    ~test() { std::cout << "デストラクタ" << std::endl; }
    void Delete() {

        delete this;  // ←許されているのなら

        std::cout << "このメッセージは有効か?" << std::endl;
        dummy = 1234;
        std::cout << dummy << " dummyへの代入参照は有効か?" << std::endl;
    }
};

int main () {
    test* t = new test;
    t->Delete();
    return 0;
}
-------[実行結果]-----------
デストラクタ
このメッセージは有効か?
1234 dummyへの代入参照は有効か?
----------------------------

PIP 2003/11/10(月) 13:00:38
>疑問2 さらに突っ込んで質問です。
>    delete this 後のインスタンスはどこまで保障されているのか?
delte this;するまで。
この後はthisポインタにアクセスしてはいけません。
(=メンバ変数、メンバ関数のアクセスがNG)

例のプログラムの場合は
dummy = 1234;
で死にます。


ちなみに
>つまり、delete this は許されているが、すべきではない。
>理由:
> auto変数としてのオブジェクトがdelete thisの可能性のあるコードを持つべきではな
>い。
とありますが、COMのようにモジュールがバイナリレベルで分割されている場合は、
delete this;
が必須になります。
使わないに越したことはないでしょうけど。

dairygoods 2003/11/10(月) 13:10:52
>  test* t = new test;
>  t->Delete();

これは、次とほぼ同じですので、thisをdeleteした時点で無効です。
void Delete(test* this) {
  delete this;
   :
}
test* t = new test;
Delete(t);

はち 2003/11/10(月) 17:46:54
[[解決]]
PIPさん dairygoodsさん ありがとうございます。

まとめ:
 delete this は許されている。C++でのコーディング上はOK
 delete this の直後からthisを参照してはいけない。(thisを参照しなければOK)
 delete this のようなコーディングスタイルはとるべきではない。


#何がOKで何がNGなのかをはっきりさせておく事が
#結構難しく、また面白いところであったり。
大変勉強になりました。ありがとうございます。

tetrapod 2003/11/11(火) 08:17:26
> delete this のようなコーディングスタイルはとるべきではない。
そんなことは無いと思います。
既に皆様のコメントにあるとおり、そう書かざるを得ない場合はあります。
単に私がやらない (やりたくない) だけ。

C/C++ はプログラマの裁量に任されているところが多く、
危険に見える行為でも、プログラマが責任を持つ限り、行ってかまいません。

delete this を行うクラスは自動変数オブジェクトを作ってはならない、
とマニュアルに明記し、プロジェクト参加メンバが全員守ってくれるなら
この種のテクニックが有用な場合は多いでしょう。

tam 2003/11/11(火) 10:59:17
自動変数オブジェクトを作って欲しくないなら
コンストラクタをprivateにして、staticな構築関数を作ればいいんでは。

class A
{
  A() {}
  ~A() {}
public:
  static A* create() { return new A; }
  void delete() { delete this; }
};

はち 2003/11/12(水) 02:28:50
tetrapodさん tamさん ありがとうございます。

>C/C++ はプログラマの裁量に任されているところが多く、
>危険に見える行為でも、プログラマが責任を持つ限り、行ってかまいません。

delete thisの(に限らず)、コーディングの危険性を理解した上で
プログラマが責任を持つのであれば、全く問題ないということ。
           ^^^^^^^^^^^

>自動変数オブジェクトを作って欲しくないなら
>コンストラクタをprivateにして、staticな構築関数を作ればいいんでは。

この方法で自動変数での潜在的な危険は回避できるというとこ。


修正:
 ×delete this のようなコーディングスタイルはとるべきではない。
 ○delete this は有用なテクニックの1つである。


# 大変勉強になります。

ぽち 2003/11/23(日) 14:24:54
今更ですが、少しだけコメントします

> delete thisの(に限らず)、コーディングの危険性を理解した上で
> プログラマが責任を持つのであれば、全く問題ないということ。
>            ^^^^^^^^^^^

責任を持たなければならないプログラマとは、そのクラスを利用する人まで含みます。
これは結構難しいことですね。

> >自動変数オブジェクトを作って欲しくないなら
> >コンストラクタをprivateにして、staticな構築関数を作ればいいんでは。
> 
> この方法で自動変数での潜在的な危険は回避できるというとこ。

自動変数だけの問題ではなくて、new[]が使われたり(これは上記構築関数で回避可
能)、派生クラスからDeleteメンバ関数が呼ばれたりする危険もあります。

本当に安全にするのであれば、共用体を使って、決して継承できないクラスにしておく
必要があるかもしれません。それに、普通にdeleteされてまずいのなら、デストラクタ
もprivateにしておくべきです。

# そもそもバイナリレベルで分割されていたとしても、delete this;なんかを使うよ
り、フレンド関数からdeleteした方がよっぽど安全です。

古いスレッドには追加発言できません。