66923


MFCのダイアログベースでプロジェクトを作り、そのダイアログでボタン押下するとサブのダイアログをモードレスで表示する機能があったとします。  
newでサブダイアログをインスタンス化するのですが、どこでdeleteすればいいのかわからず質問させていただきました。  
ボタン押下イベントのなかでnewしてdeleteするとサブダイアログは消えてしまいます。

[code]

    void CModelessDlgTestDlg::OnBnClickedButton1()
    {
        ModelessDlg* dlg = new ModelessDlg(this);
        dlg->Create(IDD_MODELESSDLG, this);
        dlg->ShowWindow(SW_SHOW);
    
        delete dlg; //ここでサブダイアログが消えてしまう。かといってdeleteしない？のはダメでしょ＾＾；
    }
    
    
[/code]

かと言って、コンストラクタでnewしておいて、デストラクタでdeleteするとしたら、複数のサブダイアログが開かれたときにメモリが足りなくなるのでは・・・  
（まぁいまのコンピュータからすれば幾多のダイアログ開かないとメモリが飛ぶことはないのでしょうが）

この場合、どこでdeleteすればいいのかわからないのでご教授いただけないでしょうか。


CC BY-SA 4.0
2020-05-22T17:04:06.117
66896
2020-05-25T00:51:23.607
38241
question
c++ mfc
モードレスダイアログをnewで表示させるけど、どこでdeleteするのか？


短い答： `delete` したくなったとき

長い答：  
モーダルは「閉じたらなくなる」ので、ユーザが閉じる操作をした後、プログラムが必要な情報を取得終了したときがデストラクタを呼ぶべき時です。なので `new`
/ `delete` を手書きせず、関数内自動変数にすると漏れがなくてよいわけです。

モードレスは実際問題として閉じてもなくならない、すなわち再表示できるブツですから「再表示できるうち」は `delete`
する必要はないです（よくあるメニューバー中のウィンドウ項目でチェックをつけたり外したりして表示・非表示を切り替える場合を想定）

結果的にメインウィンドウなり `View` なり `Doc` なりと生死を共にするのが普通で、だったら親クラスのメンバにして `Show` / `Hide`
を切り替えるとよくて、となると `new` も `delete` も出番はないです。実際オイラが過去組んだソフトでモードレスを `new` /
`delete` したことはないです（同じダイアログを複数開いたり閉じたりする場合を除く）


CC BY-SA 4.0
2020-05-22T22:39:45.780
66898
2020-05-22T22:52:04.393
2020-05-22T22:52:04.393
8589
8589
66896
answer


モードレスダイアログをどういう風に使いたいかによりますが、deleteし忘れないようにするためには、ご質問にもありますように

> コンストラクタでnewしておいて、デストラクタでdeleteする

でいいと思います。実際には、ダイアログを管理するクラス(今回だとメインダイアログ)のメンバとしてモードレスダイアログのポインタを持っておき、モードレスダイアログを開くタイミングで`new`して、管理クラスのデストラクタでポインタが`NULL`じゃなかったら`delete`する、というようなことをよくやります。

> 複数のサブダイアログが開かれたときにメモリが足りなくなるのでは・・・  
>  （まぁいまのコンピュータからすれば幾多のダイアログ開かないとメモリが飛ぶことはないのでしょうが）

Windows95とか98の時代ならともかく、常識的な範囲で複数サブダイアログを開いたくらいではメモリ不足になることはまずないと思います。


CC BY-SA 4.0
2020-05-22T23:39:36.757
66899
2020-05-22T23:39:36.757
9515
66896
answer


私ならこうします。

モードレスのダイアログといえども、閉じる操作があるはずです。そのダイアログに閉じるボタンがあるとか。  
そのボタンのアクションの中で delete this; と書きます。

delete thisを使うからには、それ以後メンバ変数を触ってはいけません。

サブダイアログを閉じないうちに、アプリケーション自体を閉じたときに問題あるかないかは、調べてみないと分かりません。

（いまからMFCですか。ご苦労様です。）


CC BY-SA 4.0
2020-05-23T14:38:02.257
66923
2020-05-23T14:38:02.257
40200
66896
answer


既に回答がありますが、サブダイアログは１つという前提での回答になっています。そのためいずれも質問文の

> 複数のサブダイアログが開かれたとき

この部分に対応できていないように見受けられます。例えばExplorerでファイルごとにプロパティダイアログを開く、その際のプロパティダイアログの管理方法をイメージしました。

これに関して、MFCは機能不足で支援できていないようです。  
まず、ダイアログクラス（`CDialog`）はコピーが禁止されています。ダイアログオブジェクトを考えたとき、コピーによってダイアログが増殖するべきでもないため、当然の対応です。  
[C++11ではムーブセマンティクス](https://cpprefjp.github.io/lang/cpp11/rvalue_ref_and_move_semantics.html)といって移動の概念が導入されましたが、MFCはそれ以前の時代のライブラリであり、ムーブセマンティクスに対応していません。  
また、MFCでは`CArray`クラスにより配列機能を提供していますが、当然ながらムーブセマンティクスに対応していないため、ダイアログオブジェクトを格納することができません。  
過去のセオリーだと、`CArray<ModelessDlg*>`クラスでポインターの管理のみを行われていたように思います。当然ポインターですので、`new`
/ `delete` は開発者が管理責任を負います。

* * *

一応、（MFCではなく）C++言語側にこれを支援する機能が提供されています。  
[`std::list`](https://ja.cppreference.com/w/cpp/container/list)であれば、ダイアログクラスのようなコピーも移動もできないクラスを扱えます。楽をするためにコンパイルオプション[`/std:c++17`](https://docs.microsoft.com/ja-
jp/cpp/build/reference/std-specify-language-standard-
version?view=vs-2019)を付けると次のように書けます。

[code]

    class CModelessDlgTestDlg {
        ...;
        std::list<ModelessDlg> dialogs;
    };
    
    void CModelessDlgTestDlg::OnBnClickedButton1() {
        auto dlg = dialogs.emplace_back(this);
        dlg.Create(IDD_MODELESSDLG, this);
        dlg.ShowWindow(SW_SHOW);
    }
    
[/code]

説明すると、[`std::list::emplace_back()`](https://ja.cppreference.com/w/cpp/container/list/emplace_back)はリスト内部で指定された引数でコンストラクターを呼び出します。またC++17以降では作成されたオブジェクトの参照を返してくれます。  
リスト内部で生成されたオブジェクトなので、リストが責任を持って管理をします。具体的にはリストが破棄されるタイミングで、全メンバーのデストラクターを呼び出してくれます。

ただし、MFC利用者はC++言語機能を極端に嫌う傾向があり、この方法が受け入れられるかはちょっとわかりません。


CC BY-SA 4.0
2020-05-23T23:43:53.320
66932
2020-05-23T23:43:53.320
4236
66896
answer


回答というか、オブジェクトの寿命について一般的な考え方を書いてみます。

モードレスDLGに限らず、ある一定期間の寿命を持つオブジェクトで他と共存するものは、  
その寿命を管理するために、所有者はそのポインタを保持しなくてはなりません。  
一般的にはオブジェクトの生殺与奪は所有者の専権事項となります。

寿命をそのオブジェクト自体に託すのであれば自己削除方法を実装します。  
この場合はやや注意が必要です。  
自己削除するオブジェクトは所有者に対して削除予定報告、又は結果報告を行う必要があります。  
そうでないと死者のメソッドを呼び出す可能性があり危険です。

なのですが、

モードレスのDLGの場合は、管理すべきは

(1) m_hWndの有効性(=ウィンドウとしての生死)  
(2) 表示/非表示(=UIとしての有効性)

であって、それのラッパーオブジェクトであるCDialogをnew
deleteする必要はほぼ無いといえます。主たるデータ構造のメンバとしておくだけで問題ありません。  
必要な時にCreateWindow系関数、DestroyWindows系関数を使って (1)を制御するか、  
ShowWindow()かSetWindowPos系で(2)を制御するだけで十分です。


CC BY-SA 4.0
2020-05-25T00:35:06.057
66953
2020-05-25T00:51:23.607
2020-05-25T00:51:23.607
3793
3793
66896
answer