【MQL5】GetPointer関数について

MQL5リファレンス
スポンサーリンク
スポンサーリンク
  1. GetPointer関数の働き・役割
  2. GetPointer関数の引数について
  3. GetPointer関数の戻り値について
    1. void* という表記について
  4. GetPointer関数を使う際の注意点
  5. GetPointer関数を使ったサンプルコード
  6. サンプルコード解説1:CItemクラス
    1. 変数について
    2. コンストラクタとデストラクタ
      1. CItem()
      2. ~CItem()
    3. メソッドについて
  7. サンプルコード解説2:CMyListクラス
    1. 変数について
    2. コンストラクタとデストラクタ
    3. メソッドについて
  8. サンプルコード解説3:InsertToBeginメソッドの処理記述
    1. 処理の詳細
      1. 1. ポインタの有効性を確認
      2. 2. リストの先頭要素を次に設定
      3. 3. リストの先頭を新しい要素に更新
      4. 4. trueを返して正常な挿入を示す
  9. サンプルコード解説4:Destroyメソッドの処理記述
    1. 処理の詳細
      1. 1. ポインタの宣言
      2. 2. ループ処理による全要素の確認
      3. 3. 現在のリスト要素をitemに設定
      4. 4. ポインタが動的かどうかの確認
      5. 5. 動的ポインタの削除
      6. 6. 自動生成オブジェクトの処理
  10. サンプルコード解説5:OnStart関数その1
    1. 処理の詳細
      1. 1. リストオブジェクトの作成
      2. 2. 自動的に生成されるオブジェクトの配列を作成
      3. 3. ポインタ変数の宣言
  11. サンプルコード解説6:OnStart関数その2
    1. 処理の詳細
      1. 1. 動的にオブジェクトを作成する
      2. 2. オブジェクトの作成成功を確認
      3. 3. オブジェクトの初期化
      4. 4. エキスパートログへの出力
      5. 5. リストの先頭に追加
  12. サンプルコード解説7:OnStart関数その3
    1. 処理の詳細
      1. 1. ループによる初期化とログ出力
      2. 2. ポインタの取得と有効性チェック
      3. 3. リストへの追加
  13. サンプルコード解説8:OnStart関数その4
    1. 処理の詳細
      1. 1. リスト先頭にもう一つ動的オブジェクトを追加
      2. 2. リスト内の全要素を削除
      3. 3. スクリプト終了後のログ確認

GetPointer関数の働き・役割

GetPointer関数は、指定されたクラスオブジェクトのポインタを取得するために使用されます。ポインタは、オブジェクトのメモリ上のアドレスを指すもので、オブジェクトのデータやメソッドに直接アクセスする際に便利です。この関数は、ポインタを取得する際に、任意のクラスオブジェクトを引数として受け取ります。

クラスオブジェクトは、通常、new演算子を使って動的に作成される場合や、オブジェクトの配列などで自動的に生成されることがあります。GetPointer関数を使用することで、これらのオブジェクトのポインタを操作することができます。

GetPointer関数の引数について

void*  GetPointer(
  any_class anyobject      // 任意のクラスオブジェクト
  );

GetPointer関数は以下のような引数を取ります。

GetPointer( any_class anyobject )

GetPointer関数の戻り値について

GetPointer関数戻り値は、指定したクラスオブジェクトのメモリ上のポインタです。ポインタvoid型として返され、これにより任意のクラス型のオブジェクトを指すことができます。

戻り値の型は void* です。これは、ポインタが任意の型を指すことができることを意味し、取得したポインタはオブジェクトの型に応じて適切にキャストして使用する必要があります。

なお、構造体や基本データ型に対してはポインタを取得できないため、これらをGetPointer関数に渡すとコンパイルエラーが発生します。また、無効なポインタを扱う場合は、プログラムが異常終了する可能性があるため、CheckPointer関数ポインタの有効性を確認する必要があります。

void* という表記について

voidという型は、通常、関数戻り値を持たないことを示すために使われます。例えば、void戻り値に指定した関数は、何も値を返さないことを意味します。しかし、void* という表記になると少し意味が変わります。

void* は、「汎用ポインタ」を指すために使われる型です。これは、特定のデータ型ではなく、どんな型のデータのアドレスも指すことができるポインタです。つまり、void* 型のポインタは、整数、浮動小数点数、文字列クラスオブジェクトなど、任意のデータ型メモリ上のアドレスを格納できます。

ただし、void* はデータ型が不明なポインタであるため、実際に使用する際には、目的に応じて正しいデータ型キャスト(型変換)する必要があります。例えば、int* 型のポインタキャストすれば整数型のデータにアクセスでき、char* にキャストすれば文字列にアクセスできるようになります。

GetPointer関数を使う際の注意点

GetPointer関数クラスオブジェクトに対してのみ使用できます。構造体や基本データ型に対してはポインタを取得できないため、それらを引数に渡すとコンパイルエラーが発生します。
また、オブジェクトがnew演算子で作成されたか、配列やその他の方法で自動的に生成されたかに関わらず、クラスオブジェクトにはポインタが存在します。しかし、配列などで自動生成されたポインタPOINTER_AUTOMATIC型であり、delete演算子を使用して解放することはできません。

無効なポインタを使用するとプログラムが異常終了する恐れがあります。たとえば、ポインタNULLであったり、オブジェクトがdelete演算子で既に削除されている場合、無効なポインタが発生します。このため、ポインタを使用する前に、CheckPointer関数を使ってポインタの有効性を確認することが推奨されます。

ポインタが無効な場合、プログラムの実行時に重大なエラーが発生する可能性があるため、特にポインタの操作には注意が必要です。

GetPointer関数を使ったサンプルコード

//+------------------------------------------------------------------+
//| リスト要素を実装するクラス                                               |
//+------------------------------------------------------------------+
class CItem
 {
  int               m_id;          // 各リスト要素の識別子を保持する変数
  string            m_comment;     // 各リスト要素に関連するコメントを保持する変数
  CItem*            m_next;        // 次のリスト要素へのポインタ
public:
                    CItem() { m_id=0; m_comment=NULL; m_next=NULL; }  // コンストラクタ:初期値として0やNULLを設定
                   ~CItem() {                                          // デストラクタ:オブジェクト破棄時に呼ばれる
                     // オブジェクトの破棄時に、ポインタが動的か自動かを判定し、エキスパートログに出力する
                     Print("Destructor of ",m_id,
                           (CheckPointer(GetPointer(this))==POINTER_DYNAMIC) ? 
                           "dynamic":"non-dynamic"); 
                    }
  void              Initialize(int id,string comm) { m_id=id; m_comment=comm; } // オブジェクトの初期化メソッド
  void              PrintMe() { Print(__FUNCTION__,":",m_id,m_comment); }      // 識別子とコメントをエキスパートログに出力する
  int               Identifier() { return(m_id); }    // 識別子を返すメソッド
  CItem*            Next() { return(m_next); }        // 次のリスト要素へのポインタを返すメソッド
  void              Next(CItem *item) { m_next=item; } // 次のリスト要素を設定するメソッド
 };
//+------------------------------------------------------------------+
//| リストの最も単純なクラス                                                 |
//+------------------------------------------------------------------+
class CMyList
 {
  CItem*            m_items;  // リストの先頭要素へのポインタを保持
public:
                    CMyList() { m_items=NULL; }  // コンストラクタ:リストの先頭をNULLで初期化
                   ~CMyList() { Destroy(); }     // デストラクタ:リストの要素を全て削除する
  bool             InsertToBegin(CItem* item);   // リストの先頭に新しい要素を挿入するメソッド
  void             Destroy();                   // リストの要素を全て削除するメソッド
 };
//+------------------------------------------------------------------+
//| 先頭にリスト要素を挿入する                                              |
//+------------------------------------------------------------------+
bool CMyList::InsertToBegin(CItem* item)
 {
  // ポインタが無効かどうかを確認し、無効なら挿入せずにfalseを返す
  if(CheckPointer(item)==POINTER_INVALID) return(false);
  
  // 現在のリストの先頭を、新しい要素の次に設定
  item.Next(m_items);
  
  // リストの先頭を新しい要素に更新
  m_items=item;

  return(true);  // 正常に挿入できた場合はtrueを返す
 }
//+------------------------------------------------------------------+
//| 要素の削除によってリストを削除する                                         |
//+------------------------------------------------------------------+
void CMyList::Destroy()
 {
  // ループの中で作業するためのポインタを宣言
  CItem* item;

  // リスト内の全ての要素を確認し、動的ポインタならdeleteで解放
  while(CheckPointer(m_items)!=POINTER_INVALID)
    {
     item=m_items;  // 現在のリスト要素をitemに設定
     m_items=m_items.Next();  // リストを次の要素に進める

    // ポインタが動的なものであれば、deleteで解放
    if(CheckPointer(item)==POINTER_DYNAMIC)
       {
        Print("Dynamic object ",item.Identifier()," to be deleted");  // 削除対象の要素をエキスパートログに出力
        delete (item);  // 動的ポインタを削除
       }
    else 
        Print("Non-dynamic object ",item.Identifier()," cannot be deleted");  // 自動的に生成されたオブジェクトは削除しない
    }
 }
//+------------------------------------------------------------------+
//| スクリプトプログラムを開始する関数                                          |
//+------------------------------------------------------------------+
void OnStart()
 {
  // リストオブジェクトを作成
  CMyList list;
  
  // 自動的に生成されるオブジェクトの配列を作成
  CItem   items[10];
  
  // ポインタ変数を宣言
  CItem*  item;

  //--- 動的にオブジェクトを作成し、リストに追加
  item=new CItem;  // new演算子で動的にオブジェクトを生成
  if(item!=NULL)   // オブジェクトの作成が成功したか確認
    {
     item.Initialize(100,"dynamic");  // 識別子100と"dynamic"というコメントで初期化
     item.PrintMe();  // エキスパートログに出力
     list.InsertToBegin(item);  // リストの先頭に追加
    }

  //--- 自動生成されたオブジェクトをリストに追加する
  for(int i=0; i<10; i++)
    {
     items[i].Initialize(i,"automatic");  // 各要素を識別子iと"automatic"で初期化
     items[i].PrintMe();  // エキスパートログに出力
     item=GetPointer(items[i]);  // ポインタを取得
    if(CheckPointer(item)!=POINTER_INVALID)  // ポインタが有効であればリストに追加
        list.InsertToBegin(item);
    }

  //--- リスト先頭にもう1つ動的オブジェクトを追加
  item=new CItem;  // new演算子で動的にオブジェクトを生成
  if(item!=NULL)   // オブジェクトの作成が成功したか確認
    {
     item.Initialize(200,"dynamic");  // 識別子200と"dynamic"というコメントで初期化
     item.PrintMe();  // エキスパートログに出力
     list.InsertToBegin(item);  // リストの先頭に追加
    }

  //--- リストの全ての要素を削除する
  list.Destroy();  // リスト内の要素を削除

  //--- スクリプト終了後に全てのリスト要素が削除される
  //--- 端末の「エキスパート」タブを参照することで、削除状況が確認できる
 }

このプログラムは、クラスCItemをリスト構造として扱い、動的に生成されたオブジェクトや自動的に生成されたオブジェクトのポインタをリストに追加・管理します。

  1. 動的オブジェクトの追加:
    new演算子動的に生成されたオブジェクト(CItemインスタンス)をリストに追加します。このオブジェクトは識別子とコメントで初期化され、エキスパートログに出力されます。
  2. 自動オブジェクトの追加:
    自動的に生成されたオブジェクト(CItemの配列)もリストに追加します。これらのオブジェクトは、自動生成されたため動的メモリとは異なりますが、同様にエキスパートログに出力されます。
  3. リスト内のオブジェクト削除:
    リスト内の動的オブジェクトを順番に削除します。動的に生成されたオブジェクトはdeleteで解放されますが、自動生成されたオブジェクトは削除されず、エキスパートログに「削除できない」と出力されます。

スクリプト実行後、端末の「エキスパート」タブに各オブジェクトの生成や削除状況が表示されます。

サンプルコード解説1:CItemクラス

/+------------------------------------------------------------------+
//| リスト要素を実装するクラス                                               |
//+------------------------------------------------------------------+
class CItem
 {
  int               m_id;          // 各リスト要素の識別子を保持する変数
  string            m_comment;     // 各リスト要素に関連するコメントを保持する変数
  CItem*            m_next;        // 次のリスト要素へのポインタ
public:
                    CItem() { m_id=0; m_comment=NULL; m_next=NULL; }  // コンストラクタ:初期値として0やNULLを設定
                   ~CItem() {                                          // デストラクタ:オブジェクト破棄時に呼ばれる
                     // オブジェクトの破棄時に、ポインタが動的か自動かを判定し、エキスパートログに出力する
                     Print("Destructor of ",m_id,
                           (CheckPointer(GetPointer(this))==POINTER_DYNAMIC) ? 
                           "dynamic":"non-dynamic"); 
                    }
  void              Initialize(int id,string comm) { m_id=id; m_comment=comm; } // オブジェクトの初期化メソッド
  void              PrintMe() { Print(__FUNCTION__,":",m_id,m_comment); }      // 識別子とコメントをエキスパートログに出力する
  int               Identifier() { return(m_id); }    // 識別子を返すメソッド
  CItem*            Next() { return(m_next); }        // 次のリスト要素へのポインタを返すメソッド
  void              Next(CItem *item) { m_next=item; } // 次のリスト要素を設定するメソッド
 };

このコードは、リスト構造を持つクラスCItemを定義しています。CItemクラスは、各リスト要素の情報を保持し、次のリスト要素へのポインタを使ってリスト全体を管理する仕組みになっています。

変数について

  • m_id
    リスト要素識別子を保持するための変数です。この識別子は、各要素が持つ一意(1つしか存在しない)のIDとして使用され、リスト内で要素を区別するために使われます。
  • m_comment
    リスト要素に関連するコメントを保持します。このコメントは、PrintMeメソッドでエキスパートログに出力され、各要素がどのようなデータを持っているかを確認するために使用されます。
  • m_next
    次のリスト要素へのポインタを保持する変数です。これにより、リスト構造を構築することができます。リストの各要素は、m_nextを通じて次の要素を指すことで、全体のリストがリンクされます。

コンストラクタとデストラクタ

CItem()


クラスコンストラクタです(コンストラクタとは、クラスインスタンスが作成される際に自動的に呼び出され、初期化を行う特殊なメソッドです)。オブジェクトが作成されたときに、m_idに0、m_commentにNULL、m_nextにNULLを設定して、初期化します。これにより、新しいオブジェクトは必ずデフォルトの値で生成されます

~CItem()


デストラクタは、オブジェクトが破棄されるときに自動的に呼び出され、オブジェクトのポインタ動的か自動かを判定してエキスパートログに出力します。この内部で行われている処理を、さらに詳細に説明します。

thisは、現在のオブジェクト自体を指す特殊なキーワードです。オブジェクトのメンバ関数デストラクタの内部で使用されると、その関数が所属するオブジェクト(インスタンス)を参照します。thisを使うことで、CItemクラスデストラクタ内で現在処理しているオブジェクトのポインタを取得することができます。

ここでは、GetPointer関数を使って、デストラクタが呼び出されているCItemオブジェクト自体のポインタを取得しています。

GetPointer関数は、指定されたクラスオブジェクトのポインタを取得するための関数です。この場合、this(現在のオブジェクト)を引数に渡して、CItemオブジェクトのメモリ上のアドレスを取得しています。ポインタとは、オブジェクトがメモリ上でどこに格納されているかを示す値です。

CheckPointer関数は、ポインタが有効かどうか、さらにそのポインタ動的なものか自動的なものかを確認するために使用されます。
具体的には、以下のような判定を行います。

この関数によって、オブジェクトのポインタが正しいかどうか、そしてそのポインタ動的なものであるかどうかを確認します。

三項演算子は、条件式 式1 式2の形式で使用され、条件式が真であれば式1を、偽であれば式2を返す構文です。

ここで使用されている三項演算子は以下のように働きます。

CheckPointer関数の結果がPOINTER_DYNAMICが真の場合は、文字列dynamicを返します。
それ以外の場合(自動生成されたオブジェクトの場合)は、文字列non-dynamicを返します。

Print関数は、与えられた情報をエキスパートログに出力する関数です。このデストラクタ内では、以下の情報をログに出力しています。

  • m_id: このオブジェクトの識別子。どのオブジェクトが破棄されているかを表します。
  • dynamicまたはnon-dynamic: オブジェクトのポインタ動的か自動かを示すメッセージ。

処理の詳細な流れとしては、まずGetPointer関数を呼び出して、現在のCItemオブジェクトのポインタを取得します。次に、取得したポインタCheckPointer関数に渡して、ポインタ動的か自動かを確認します。三項演算子を使用して、ポインタ動的であればdynamic、そうでなければnon-dynamicという文字列を選択します。最終的に、Print関数を使って、オブジェクトのID(m_id)とポインタのタイプ(dynamicまたはnon-dynamic)をエキスパートログに出力します。

これにより、CItemオブジェクトが破棄される際、そのオブジェクトが動的に生成されたものか、自動的に生成されたものかがログに記録されます。

メソッドについて

  • Initialize(int id, string comm)(メソッドとは、クラス内で定義される関数で、オブジェクトの振る舞いを定義するものです)
    このメソッドは、オブジェクトの初期化に使用されます。識別子idとコメントcommを設定し、それぞれm_idとm_commentに格納します。リスト要素の情報を指定するために使われます。
  • PrintMe()
    このメソッドは、オブジェクトの識別子とコメントをエキスパートログに出力します。__FUNCTION__は現在実行中の関数名を返すため、ログには「PrintMe」という関数名とともに、m_idおよびm_commentの値が表示されます。
  • Identifier()
    識別子であるm_idを返すメソッドです。他のコードから、このメソッドを呼び出してリスト要素のIDを取得することができます。
  • Next()
    リストの次の要素へのポインタを返すメソッドです。このメソッドを使って、リストを次の要素に進めることができます。
  • Next(CItem *item)
    次のリスト要素を設定するためのメソッドです。このメソッドを使って、現在の要素から次の要素へのリンクを作成します。

CItemクラスは、リスト要素の基本的な情報(識別子とコメント)と、リストの次の要素を指すポインタを保持します。これにより、リスト全体を構成する複数の要素を、次々と連結して管理できるようになっています。

サンプルコード解説2:CMyListクラス

//+------------------------------------------------------------------+
//| リストの最も単純なクラス                                                 |
//+------------------------------------------------------------------+
class CMyList
 {
  CItem*            m_items;  // リストの先頭要素へのポインタを保持
public:
                    CMyList() { m_items=NULL; }  // コンストラクタ:リストの先頭をNULLで初期化
                   ~CMyList() { Destroy(); }     // デストラクタ:リストの要素を全て削除する
  bool             InsertToBegin(CItem* item);   // リストの先頭に新しい要素を挿入するメソッド
  void             Destroy();                   // リストの要素を全て削除するメソッド
 };

このコードは、非常にシンプルなリスト構造を管理するクラスであるCMyListを定義しています。CMyListクラスは、リストの先頭要素を保持し、新しい要素をリストに挿入したり、リスト全体を削除したりする機能を持っています。

変数について

m_itemsという変数は、リストの先頭要素へのポインタを保持します。リストは複数のCItemオブジェクトで構成されており、各オブジェクトは次の要素へのポインタ(m_next)を持っています。m_itemsは、そのリストの最初の要素を指すため、リスト全体のエントリポイントとなります。

コンストラクタとデストラクタ

CMyList()はクラスコンストラクタです。m_itemsをNULLで初期化して、リストが空であることを示しています。リストが空の状態とは、先頭の要素が存在しない状態です。この初期化により、リストが最初は何も要素を持たないことが保証されます。

~CMyList()はクラスデストラクタです。このデストラクタは、Destroyメソッドを呼び出して、リスト内の全ての要素を削除します。Destroyメソッドは、リストの中にある全てのCItemオブジェクトを順次削除することで、リストのメモリを解放します。デストラクタは、クラスインスタンスが破棄されるときに自動的に呼び出されるため、メモリリークを防ぐ重要な役割を果たします。

メソッドについて

InsertToBegin(CItem* item)は、リストの先頭に新しい要素を挿入する役割を持っています。引数として受け取るitemは、リストに追加されるCItemオブジェクトへのポインタです。このメソッドでは、まず追加する要素のm_nextを、現在のリストの先頭要素であるm_itemsに設定します。次に、m_itemsを新しく追加された要素に更新することで、その要素がリストの先頭となります。このようにして、リストの構造を維持しながら新しい要素が追加されていきます。

Destroy()は、リスト内の全ての要素を削除するために使用されます。リストの各要素動的メモリ上に配置されている場合があるため、リスト全体を削除するには各要素メモリを解放する必要があります。Destroyメソッドはリストをたどりながら、各CItemオブジェクトを適切に解放します。

サンプルコード解説3:InsertToBeginメソッドの処理記述

//+------------------------------------------------------------------+
//| 先頭にリスト要素を挿入する                                              |
//+------------------------------------------------------------------+
bool CMyList::InsertToBegin(CItem* item)
 {
  // ポインタが無効かどうかを確認し、無効なら挿入せずにfalseを返す
  if(CheckPointer(item)==POINTER_INVALID) return(false);
  
  // 現在のリストの先頭を、新しい要素の次に設定
  item.Next(m_items);
  
  // リストの先頭を新しい要素に更新
  m_items=item;

  return(true);  // 正常に挿入できた場合はtrueを返す
 }

InsertToBeginメソッドは、CMyListクラスのリストの先頭に新しいリスト要素を挿入するためのメソッドです。このメソッドを使用することで、新しいCItemオブジェクトをリストの最初に追加することができます。

処理の詳細

このメソッドは、引数としてCItem型のポインタitemを受け取ります。itemは、リストに追加される新しい要素を指します。次に、メソッド内の処理を詳しく見ていきます。

1. ポインタの有効性を確認

最初に、CheckPointer関数を使って、itemが指すポインタが有効かどうかを確認します。CheckPointer関数は、ポインタが無効(POINTER_INVALID)であれば、falseを返して処理を中断します。これにより、無効なポインタがリストに挿入されることを防ぎます。

2. リストの先頭要素を次に設定

次に、itemの次の要素(m_next)を、現在のリストの先頭要素(m_items)に設定します。これは、現在の先頭要素が新しい要素の後に続くことを意味します。この操作によって、既存のリストの構造を維持しながら、新しい要素がリストの最前に挿入される準備が整います。

3. リストの先頭を新しい要素に更新

次に、m_itemsを、新しく追加された要素であるitemに更新します。これにより、itemがリストの新しい先頭要素として設定され、リスト全体の先頭が更新されます。これで新しい要素がリストの最前に追加されたことになります。

4. trueを返して正常な挿入を示す

最後に、リストに新しい要素を正常に挿入できた場合、trueを返します。これにより、呼び出し元のコードは挿入が成功したかどうかを確認することができます。

InsertToBeginメソッドは、新しい要素をリストの先頭に追加するための簡単で効率的な方法を提供しています。ポインタの有効性をチェックし、リストの構造を保ちながら新しい要素を追加することで、リストが正しく機能することを保証します。

サンプルコード解説4:Destroyメソッドの処理記述

//+------------------------------------------------------------------+
//| 要素の削除によってリストを削除する                                         |
//+------------------------------------------------------------------+
void CMyList::Destroy()
 {
  // ループの中で作業するためのポインタを宣言
  CItem* item;

  // リスト内の全ての要素を確認し、動的ポインタならdeleteで解放
  while(CheckPointer(m_items)!=POINTER_INVALID)
    {
     item=m_items;  // 現在のリスト要素をitemに設定
     m_items=m_items.Next();  // リストを次の要素に進める

    // ポインタが動的なものであれば、deleteで解放
    if(CheckPointer(item)==POINTER_DYNAMIC)
       {
        Print("Dynamic object ",item.Identifier()," to be deleted");  // 削除対象の要素をエキスパートログに出力
        delete (item);  // 動的ポインタを削除
       }
    else 
        Print("Non-dynamic object ",item.Identifier()," cannot be deleted");  // 自動的に生成されたオブジェクトは削除しない
    }
 }

Destroyメソッドは、CMyListクラスのリスト内に存在するすべての要素を削除するためのメソッドです。このメソッドは、動的に生成された要素を適切に解放し、自動生成された要素は削除せずに保持します。以下、その詳細な処理を解説します。

処理の詳細

このメソッドは、リスト全体を順次処理しながら、各要素を確認して削除していきます。CItemオブジェクトがリストに保持されているため、そのポインタを使ってメモリ管理を行います。

1. ポインタの宣言

まず、itemというポインタを宣言します。このポインタは、現在処理中のリスト要素を指すために使用されます。

2. ループ処理による全要素の確認

次に、m_itemsが無効なポインタPOINTER_INVALID)でない限り、リスト全体を順次たどるためのループが実行されます。このループは、リストの先頭から次の要素へ進んでいくことで、全ての要素を確認します。

3. 現在のリスト要素をitemに設定

m_itemsが指す現在のリスト要素をitemに設定します。これにより、itemポインタは現在のリスト要素を指し示します。その後、m_itemsを次の要素に進めることで、リスト全体をたどっていくことができます。

4. ポインタが動的かどうかの確認

CheckPointer関数を使って、itemポインタが指す要素動的に生成されたものであるかどうかを確認します。POINTER_DYNAMICであれば、その要素new演算子を使って動的に生成されたものであり、deleteを使ってメモリを解放する必要があります。

5. 動的ポインタの削除

もしCheckPointer関数の結果がPOINTER_DYNAMICであれば、その要素動的に生成されたものなので、deleteを使ってメモリを解放します。その際、エキスパートログには削除対象のオブジェクトの識別子(m_id)が出力されます。

6. 自動生成オブジェクトの処理

もしポインタ動的なものでなければ(POINTER_AUTOMATICなど)、そのオブジェクトはスタックメモリ上で自動的に管理される領域)上に自動的に生成されたものであり、deleteを使って解放することはできません。この場合、削除されない旨をエキスパートログに出力し、次の要素の処理に進みます。

Destroyメソッドは、リスト内の動的に生成された要素を順次削除し、メモリリークを防ぐ重要な役割を担っています。一方で、自動生成された要素deleteできないため、それらを適切に処理するためのロジックも組み込まれています。

サンプルコード解説5:OnStart関数その1

//+------------------------------------------------------------------+
//| スクリプトプログラムを開始する関数                                          |
//+------------------------------------------------------------------+
void OnStart()
 {
  // リストオブジェクトを作成
  CMyList list;
  
  // 自動的に生成されるオブジェクトの配列を作成
  CItem   items[10];
  
  // ポインタ変数を宣言
  CItem*  item;

OnStart関数は、スクリプトが実行された際に最初に呼び出される関数です。この関数では、リストオブジェクトやアイテムの配列を作成し、リストに新しい要素を追加していく処理を行います。

処理の詳細

1. リストオブジェクトの作成

まず、CMyListクラスのオブジェクトであるlistが作成されます。このオブジェクトは、リスト構造を管理するためのものです。リストオブジェクトは、リストの要素を追加したり削除したりするために使用されます。

2. 自動的に生成されるオブジェクトの配列を作成

次に、CItemクラスインスタンスを10個持つ配列itemsが作成されます。この配列の各要素は、自動的に生成されるCItemオブジェクトを表します。配列内のオブジェクトはスタックメモリ上で自動的に管理される領域)上に配置され、特定の識別子やコメントを持つように初期化されます。自動生成されたオブジェクトは、メモリ管理が自動で行われるため、手動で解放する必要はありません。

3. ポインタ変数の宣言

CItem* itemというポインタ変数が宣言されます。このポインタは、リストに新しく追加される要素を指すために使用されます。この後の処理で、動的に生成されたオブジェクトや自動生成されたオブジェクトがこのポインタに割り当てられ、リストに挿入されていきます。

OnStart関数は、リストの作成、オブジェクトの初期化、ポインタの準備といった基本的な処理を行います。特に、動的なオブジェクトと自動的なオブジェクトの両方を管理するための重要な準備段階として機能します。この段階で生成されたリストとオブジェクトは、後続の処理で使用されます。

サンプルコード解説6:OnStart関数その2

  //--- 動的にオブジェクトを作成し、リストに追加
  item=new CItem;  // new演算子で動的にオブジェクトを生成
  if(item!=NULL)   // オブジェクトの作成が成功したか確認
    {
     item.Initialize(100,"dynamic");  // 識別子100と"dynamic"というコメントで初期化
     item.PrintMe();  // エキスパートログに出力
     list.InsertToBegin(item);  // リストの先頭に追加
    }

この部分のコードでは、new演算子を使って動的にオブジェクトを作成し、そのオブジェクトをリストに追加しています。

処理の詳細

1. 動的にオブジェクトを作成する

item = new CItem;という行で、new演算子を使ってCItemクラスのオブジェクトを動的に生成しています。

new演算子とは
new演算子は、動的メモリを割り当ててオブジェクトを生成するために使用されます。動的に作成されたオブジェクトは、プログラムが実行中の間に必要に応じてメモリを確保し、その後、手動でメモリを解放する必要があります。
この手動でメモリを解放する操作は、後でdelete演算子を使って行います。 例えば、ローカル変数として定義されたオブジェクトは関数のスコープ(変数やオブジェクトが有効な範囲)を抜けると自動的にメモリが解放されますが、new演算子で生成されたオブジェクトはスコープに依存せず、プログラムが動作している間メモリに存在し続けます。
そのため、動的メモリを使用する際は、不要になったオブジェクトを確実にdeleteすることが重要です。

2. オブジェクトの作成成功を確認

if(item != NULL)という条件で、new演算子によってオブジェクトが正しく作成されたかどうかを確認しています。newが失敗した場合、itemにはNULLが返されますが、このケースはメモリ不足や異常な状況で発生する可能性があります。そのため、オブジェクトの作成が成功したかどうかを確認するのは重要です。

3. オブジェクトの初期化

item.Initialize(100, “dynamic”);で、新しく生成されたitemを初期化しています。Initializeメソッドを使って、識別子100と”dynamic”というコメントを設定しています。これにより、オブジェクトがどのような情報を持っているかが明確になります。

4. エキスパートログへの出力

item.PrintMe();を使用して、オブジェクトの情報をエキスパートログに出力します。この行は、現在のオブジェクトの状態(識別子とコメント)をログに記録し、デバッグや状態確認に役立ちます。

5. リストの先頭に追加

最後に、list.InsertToBegin(item);によって、新しく作成したitemをリストの先頭に挿入しています。このメソッドは、CMyListクラス内のInsertToBeginメソッドを呼び出し、リストの先頭に要素を追加します。動的に作成されたオブジェクトは、リストの他の要素とリンクされ、リスト構造が更新されます。

この部分では、new演算子を使って動的にオブジェクトを作成し、そのオブジェクトをリストに追加しています。動的メモリを割り当てる場合、手動で解放する必要があるため、newで生成されたオブジェクトの管理には注意が必要です。このセクションでは、生成したオブジェクトの初期化やエキスパートログへの出力、リストへの追加が行われており、基本的な動的メモリ管理の手順が示されています。

サンプルコード解説7:OnStart関数その3

//--- 自動生成されたオブジェクトをリストに追加する
  for(int i=0; i<10; i++)
    {
     items[i].Initialize(i,"automatic");  // 各要素を識別子iと"automatic"で初期化
     items[i].PrintMe();  // エキスパートログに出力
     item=GetPointer(items[i]);  // ポインタを取得
    if(CheckPointer(item)!=POINTER_INVALID)  // ポインタが有効であればリストに追加
        list.InsertToBegin(item);
    }

この部分のコードでは、自動生成されたオブジェクトをリストに追加しています。各オブジェクトは配列として生成され、その後、リストに順次追加されます。この処理を通じて、複数のオブジェクトを管理するリストが構築されます。

処理の詳細

1. ループによる初期化とログ出力

for文を使って、items配列の各要素識別子iとautomaticというコメントで初期化しています。このループは、配列内の全10個のオブジェクトに対して行われ、識別子にはループカウンタのiが使用されます。

各オブジェクトはInitializeメソッドで初期化され、PrintMeメソッドでエキスパートログに出力されます。これにより、各オブジェクトの情報がログに表示され、処理の状態を確認することができます。

2. ポインタの取得と有効性チェック

初期化された各オブジェクトのポインタGetPointer関数で取得します。これは、配列内の各オブジェクトがメモリ上のどこに配置されているかを指し示すポインタです。取得したポインタの有効性はCheckPointer関数で確認されます。もしポインタが無効であれば、それ以上の処理は行わずに次の要素に進みます。

3. リストへの追加

ポインタが有効であれば、InsertToBeginメソッドを使ってリストの先頭にオブジェクトを追加します。この操作により、各要素が順番にリストの先頭に挿入され、リストが構築されていきます。

この部分は、自動生成された複数のオブジェクトを順次リストに追加する処理を行っています。ポインタの有効性を確認しながら、各オブジェクトをリストに挿入することで、効率的にリスト構造を管理しています。配列とリストを連携させて使うことで、多数のオブジェクトを扱う場面での効率的なデータ管理が可能です。

サンプルコード解説8:OnStart関数その4

 //--- リスト先頭にもう1つ動的オブジェクトを追加
  item=new CItem;  // new演算子で動的にオブジェクトを生成
  if(item!=NULL)   // オブジェクトの作成が成功したか確認
    {
     item.Initialize(200,"dynamic");  // 識別子200と"dynamic"というコメントで初期化
     item.PrintMe();  // エキスパートログに出力
     list.InsertToBegin(item);  // リストの先頭に追加
    }

  //--- リストの全ての要素を削除する
  list.Destroy();  // リスト内の要素を削除

  //--- スクリプト終了後に全てのリスト要素が削除される
  //--- 端末の「エキスパート」タブを参照することで、削除状況が確認できる
 }

このコードの最後の部分では、リストにもう一つの動的オブジェクトを追加し、リスト全体を削除する処理が行われています。以下、その処理内容を解説します。

処理の詳細

1. リスト先頭にもう一つ動的オブジェクトを追加

まず、new演算子を使って新しいCItemオブジェクトを動的に生成しています。生成が成功した場合は、識別子200と”dynamic”というコメントで初期化され、その後、エキスパートログに出力されます。このオブジェクトも、先ほどと同様にInsertToBeginメソッドを使ってリストの先頭に追加されます。

この部分で重要なのは、動的に生成されたオブジェクトが追加される点です。new演算子で生成されたオブジェクトは手動で解放しなければならないため、メモリリークを防ぐために、後で必ず削除する必要があります。

2. リスト内の全要素を削除

リスト内に追加されたすべての要素を削除するために、Destroyメソッドが呼ばれます。このメソッドは、リスト内の各要素を確認し、動的に生成されたオブジェクトを解放します。

Destroyメソッドは、動的に生成されたオブジェクト(new演算子で生成されたもの)をdelete演算子で解放しますが、自動的に生成されたオブジェクトは解放されずにそのまま残ります。リストに含まれる全ての要素がこのメソッドで処理されるため、最後にはメモリが適切に管理されることが保証されます。

3. スクリプト終了後のログ確認

スクリプトが終了すると、リスト内の全ての要素が削除された状態になります。削除状況は、端末の「エキスパート」タブで確認することができます。これにより、動的に生成されたオブジェクトが正しく解放されたかどうかを確認できます。

このコードの最終部分では、動的に生成されたもう一つのオブジェクトをリストに追加し、その後リスト全体を削除する処理が行われます。Destroyメソッドを使うことで、メモリリークを防ぎ、動的オブジェクトのメモリ管理が適切に行われます。また、スクリプト終了後にログを確認することで、処理が正しく行われたかを確認できます。

タイトルとURLをコピーしました