[C++Builder] TListの項目が削除されたときに、自動的にメモリを解放するには

TListは項目が変更(追加・削除・抽出)されたときにNotifyメソッドが呼ばれます。
TListクラスを継承してNotifyメソッドを上書きすれば、項目が変更されたときに様々な処理を行うことができます。

次のTList1クラスは、TMyItemを項目に持ちます。
項目が削除されたときにメモリを解放します。

//サンプルコードに使用する型
struct TMyItem
{
  TMyItem(TDateTime ATime) : Time(FormatDateTime("yyyymmddhhnnss", ATime)){}
  UnicodeString Time;
};

//TListを継承してNotifyをオーバーライトしたクラス
//項目が削除されたらメモリを解放する
class TMyList1 : public TList
{
protected:
  virtual void __fastcall Notify(void * Ptr, TListNotification Action)
  {
    switch (Action) {
      case lnDeleted: //エントリがリストから削除された
        delete (TMyItem*)Ptr;
        break;
    }
  }
};

もう一工夫してみます。

Notifyメソッドをイベントにして、処理を簡単に差し替えることができるようにします。

次のTMyList2クラスはOnNotifyプロパティに、項目が変更されたときの処理を設定できます。

class TMyList2 : public TList
{
private:
  typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender, void * Ptr, TListNotification Action);
  TNotifyEvent FOnNotify;
protected:
  //項目が変更されたとき
  virtual void __fastcall Notify(void * Ptr, TListNotification Action)
  {
    //イベントが設定されているときは実行する
    if (FOnNotify != NULL) { FOnNotify(this, Ptr, Action); }
  }
public:
  __fastcall TMyList2(void) : TList(), FOnNotify(NULL) { }
  __fastcall virtual ~TMyList2() { Clear(); }
  typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender, void * Ptr, TListNotification Action);
  //項目が変更されたときに実行するイベント
  __property TNotifyEvent OnNotify = {read=FOnNotify, write=FOnNotify};
};

サンプルプログラムでは、TMyList2クラスを使用して、項目が変更されたときにMemoコンポーネントに表示します。

TList_Notify01

class TForm1 : public TForm
{
…
private:    // ユーザー宣言
  TMyList2* FList;
  void __fastcall OnListNofity(System::TObject* Sender, void * Ptr, TListNotification Action);
…
}

__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner), FList(new TMyList2())
{
  Memo1->Clear();
  this->FList->OnNotify = this->OnListNofity;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::OnListNofity(System::TObject* Sender, void * Ptr, TListNotification Action)
{
  TMyItem* item = (TMyItem*)Ptr;
  switch (Action) {
    case lnAdded: //エントリがリストに追加された
      Memo1->Lines->Add("追加:" + item->Time);
      break;
    case lnDeleted: //エントリがリストから削除された
      Memo1->Lines->Add("削除:" + item->Time);
      delete item;
      break;
    case lnExtracted: //エントリがリストから抽出された
      Memo1->Lines->Add("抽出:" + item->Time);
      break;
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonAddClick(TObject *Sender)
{
  FList->Add(new TMyItem(Now()));
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonRemoveClick(TObject *Sender)
{
  FList->Delete(0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonExtractedClick(TObject *Sender)
{
  FList->Extract(FList->Items[0]);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonClearClick(TObject *Sender)
{
  FList->Clear();
}

TList_Notify02

2013年7月14日追記:コメント欄で教えていただいた点を修正しました。

コメント

  1. lnExtractedが実行される条件がいまいちわかりませんでした。
    ButtonExtractedの処理については、lnDeletedとlnAddedが順番に呼ばれるようですね。

    lnExtractedは参照で呼ばれるとのことで、以下のようなコードも試したのですが、lnExtractedの処理とはなりませんでした。

    —-
    TMyItem *pList;
    pList = (TMyItem *)FList->Items[0];
    String str = pList->Time;
    Memo1->Lines->Add(str);
    —-

    よろしければ、lnExtractedが呼ばれる処理についても教えていただければと思います。

  2. FList->Extract()を使って処理したものに関して、lnExtractedの処理を通ることが確認できました。

    指定の項目をリストから削除したい時に使うのですね。

  3. 山本隆様
    サンプルコードの修正ありがとうございました。
    勉強になります。

  4. 別のThreadでデータ処理をして、
    MainスレッドでOnNotifyイベントを監視し、データの表示処理を行おうと思っておりますので、
    TThreadListを使ったほうが良いでしょうか?

  5. OnListNofity中の処理が終わらないと、帰ってこないようでした。
    複数のスレッドが同時にアクセスすることがないので、TListでもOKでしょう。

  6. Delphiは問題ないようですが、
    C++ Builder XE4 Firemonkey HD app では動かないようですね

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください