MQL5でのオブジェクトポインターの使用

7 10月 2015, 16:01
MetaQuotes
0
555

はじめに

MQL5では、コード内のクラスタイプ変数をさらに使用するために自分自身のクラスを作成できます。本記事MQL5でのオブジェクト作成と破壊の順 で学んだ通り、 構造とクラスは自動と動的の2通りで作成できます。

自動でオブジェクト作成するには単にクラスタイプ 変数を宣言します。 - システムが自動でそれを作成し初期化がします。動的にオブジェクトを作成するには 、演算子 new を明示的にオブジェクトポイントに適用する必要があります。

しかし、 自動で作成されたオブジェクトと動的にで作成されたオブジェクトの違いとは何か、そしていつオブジェクトポイントターを使う必要があり、いつ自動でオブジェクトを作成するのに十分なのでしょうか?このトピックが本記事の主題です。最初に、オブジェクトを使用する際の落とし穴とその対処法を考えましょう。


無効ポインターへのアクセスのクリティカルエラー

まず、オブジェクトポインター を使用する際、使用の前にオブジェクトの初期化が必要であることを覚えておかなければなりません。無効ポインターにアクセスすると、クリティカルエラーでMQLプログラムが終了し、プログラムが削除されます。例として、 そこで宣言されたクラス CHelloのシンプルなExpert Advisorを考えましょう。クラスインスタンス へのポインターはグローバルレベルで宣言されます。

//+------------------------------------------------------------------+
//|                                             GetCriticalError.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| A simple class                                                   |
//+------------------------------------------------------------------+
class CHello
  {
private:
   string            m_message;
public:
                     CHello(){m_message="Starting...";}
   string            GetMessage(){return(m_message);}
  };
//---
CHello *pstatus;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- calling a method to show status
   Print(pstatus.GetMessage());
//--- printing a message if Expert Advisor has been initialized successfully
   Print(__FUNCTION__," The OnInit() function is completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

pstatus 変数はオブジェクトポインターですが、 new 演算子を使って意図的にオブジェクト自身を作成するのを「忘れます」。 EURUSD チャートでこのExpert Advisor を立ち上げようとする場合、 当然な結果を見ます。- Expert AdvisorがOnInit() 関数の実行ステージで即時にアンロードされました。Experts Journalに表示されるメッセージは以下です。:

14:46:17 Expert GetCriticalError (EURUSD, H1) loaded successfully
14:46:18 Initializing of GetCriticalError (EURUSD, H1) failed
14:46:18 Expert GetCriticalError (EURUSD, H1) removed

この例はとてもシンプルでエラーを見つけることが簡単です。しかし、あなたのMQL5プログラムが数百または数千のラインコードを含む場合、これらのエラーを見つけることはかなり難しいかもしれません。 例えば、特有のマーケット構造など、特にプログラムの作動の条件が予測できない要因に左右されるような緊急状況のケースで重要です。


使用前にポインターを確認する利点

クリティカルプログラム終了を防ぐことは可能でしょうか?もちろんです!使用前にオブジェクトポイント確認を挿入すれば十分です。PrintStatus関数を追加してこの例を修正しましょう。:

//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void PrintStatus(CHello *pobject)
  {
   if(CheckPointer(pobject)==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(pobject.GetMessage());
  }

この関数は、GetMessage() 方法を呼びます。CHello タイプのオブジェクトポインターはその関数に渡されました。まず、CheckPointer()関数を使ってポインターをチェックしましょう。 外部パラメータを追加し、Expert AdvisorのコードをGetCriticalError_OnDemand.mq5ファイルに保存しましょう。

//+------------------------------------------------------------------+
//|                                    GetCriticalError_OnDemand.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"

input bool GetStop=false;// To get a critical error
//+------------------------------------------------------------------+
//| A simple class                                                   |
//+------------------------------------------------------------------+
class CHello
  {
private:
   string            m_message;
public:
                     CHello(){m_message="Starting...";}
   string            GetMessage(){return(m_message);}
  };
//---
CHello *pstatus;
//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void PrintStatus(CHello *pobject)
  {
   if(CheckPointer(pobject)==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(pobject.GetMessage());
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- calling a method to show status
   if(GetStop)
      pstatus.GetMessage();
   else
      PrintStatus(pstatus);
//--- printing a message if Expert Advisor has been initialized successfully
   Print(__FUNCTION__," The OnInit() function is completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

これで、Expert Advisorを2つの方法で立ち上げられます。:

  1. クリティカルエラーで (GetStop = true)
  2. エラーはないが、無効ポインターについてのメッセージで (GetStop = false)

デフォルトでは、 Expert Advisorの実行が成功だと、以下のメッセージが"Experts" Journalに表れます。 :

GetCriticalError_OnDemand (EURUSD, H1) 15:01:57 PrintStatus the variable 'object' isn't initialized!
GetCriticalError_OnDemand (EURUSD, H1) 15:01:57 OnInit function OnInit () is completed

このようにして、使用前のポインターの確認でクリティカルエラーを防ぐことができます。

その関数で使用する前にポインターが正しい か常に確認します。


レファレンスによる非初期化オブジェクトの渡し

関数の入力パラメータとして非初期化オブジェクトを渡すとどうなるか?(オブジェクトポイントではなく、レファレンスによるオブジェクト自身)クラスなどの複雑なオブジェクトと構造はレファレンスによってアンパサンドと共に渡されます。GetCriticalError_OnDemand.mq5.のコードを書き直しましょう。PrintStatus() 関数の名前を変更し、コードを別のやり方で書きなおします。

//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void UnsafePrintStatus(CHello &object)
  {
   DebugBreak();
   if(CheckPointer(GetPointer(object))==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(object.GetMessage());
  }

違いは、このタイプの変数自身がCClassHelloタイプのオブジェクトポインターではなく入力パラメータとしてレファレンスによって渡された事です。Expert Advisorの新しいバージョンをGetCriticalError_Unsafe.mq5として保存しましょう。

//+------------------------------------------------------------------+
//|                                      GetCriticalError_Unsafe.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"

input bool GetStop=false;// To get a critical error
//+------------------------------------------------------------------+
//| A simple class                                                   |
//+------------------------------------------------------------------+
class CHello
  {
private:
   string            m_message;
public:
                     CHello(){m_message="Starting...";}
   string            GetMessage(){return(m_message);}
  };
//---
CHello *pstatus;
//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void UnsafePrintStatus(CHello &object)
  {
   DebugBreak();
   if(CheckPointer(GetPointer(object))==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(object.GetMessage());
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- calling a method to show status
   if(GetStop)
      pstatus.GetMessage();
   else
      UnsafePrintStatus(pstatus);
//--- printing a message if Expert Advisor has been initialized successfully
   Print(__FUNCTION__," The OnInit() function is completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

GetCriticalError_OnDemand.mq5 とGetCriticalError_Unsafe.mq5 の間でExpert Advisorの関数へのパラメータの渡し方に違いが見えます。最初のケースでは、 オブジェクトポインターはその関数に渡され、第二のケースではオブジェクト自身がレファレンスによって渡されます。オブジェクトとポインターの使用前に、両方のケースで、関数がポインターの正しさを確認します。

それはExpert Advisorが同じように働くことを意味するのか?いいえ、そうではありません!パラメータ GetStop=falseでExpert Advisorを立ち上げ、クリティカルエラーを入手しましょう。オブジェクトがレファレンスによって渡されると、クリティカルエラーが関数呼び出しステージで起こり、非初期化オブジェクトがパラメータとして渡されます。F5 ボタンを使ってMetaEditor5から直接デバッグモードでスクリプトを立ち上げることでこれを検証できます。

手動ブレークポイントの使用を避けるため、DebugBreak () その関数のブレークポイントを追加することによってその関数を修正しましょう。

//+------------------------------------------------------------------+
//| Prints a message using a method of CHello type object            |
//+------------------------------------------------------------------+
void UnsafePrintStatus(CHello &object)
  {
   DebugBreak();
   if(CheckPointer(GetPointer(object))==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(object.GetMessage());
  }

デバッグモードでは、Expert Advisorが、DebugBreak() 関数の呼び出しの前にアンロードされます。レファレンスによって非初期化オブジェクトが渡される場合、 どのようにセキュア コードを書くのか ?答えはシンプルです。 - 関数ローディングを使用します。

もし非初期化オブジェクトのポインターが関数のパラメータとしてレファレンスによって渡される場合、 クリティカルエラーになり、mql5-プログラムが停止します。


安全なコードのためのOverloaded関数の使用

もし外部ライブラリを使う開発が、入力オブジェクトが正しか確認することを強制していたとしたら とても不便です。ライブラリですべての必要なチェックを実行する方がいいです。関数オーバーローディングを使ってそれを実行できます。

関数オーバーローディングと共にUnsafePrintStatus() 方法を実行し、この関数の2つのバージョンを書きましょう。- 1つ目はオブジェクト自身ではなく渡されたオブジェクトポインター を使い、2つ目はレファレンスによるオブジェクト渡しを使います。両方の関数は同じ名前 「PrintStatus」を持ちますが、この実行はもはや潜在的に危険ではありません。

//+------------------------------------------------------------------+
//| The safe printing of a message using the CHello object pointer     |
//+------------------------------------------------------------------+
void SafePrintStatus(CHello *pobject)
  {
   if(CheckPointer(pobject)==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(pobject.GetMessage());
  }
//+------------------------------------------------------------------+
//| Printing a message using the CHello object, passed by reference  |
//+------------------------------------------------------------------+
void SafePrintStatus(CHello &pobject)
  {
   DebugBreak();
   SafePrintStatus(GetPointer(pobject));
  }

その関数がオブジェクトポインターを渡すことによって呼ばれる場合のケースでは、正しさ の確認が実行され、クリティカルエラーが起こりません。レファレンスによってオブジェクトを渡すことで関数を呼ぶ場合、まずGetPointer 関数を使ってオブジェクトポイントを入手し、 それからポインターによるオブジェクト渡しを使用する安全なコードを呼び出します。

以下に代わってセキュアバージョンの関数をさらに短く書き直せます。

void SafePrintStatus (CHello & pobject)
  (
   DebugBreak ();
   CHello * p = GetPointer (pobject);
   SafePrintStatus (p);
  )

上記ではなくコンパクトなバージョンを書きましょう。

void SafePrintStatus (CHello & object)
  (
   DebugBreak ();
   SafePrintStatus (GetPointer (object));
  )

両方の バージョンは対等です。第二のバージョンをGetCriticalError_Safe.mq5の名前で保存しましょう。

//+------------------------------------------------------------------+
//|                                        GetCriticalError_Safe.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"

input bool GetStop=false;// To get a critical error
//+------------------------------------------------------------------+
//| A simple class                                                   |
//+------------------------------------------------------------------+
class CHello
  {
private:
   string            m_message;
public:
                     CHello(){m_message="Starting...";}
   string            GetMessage(){return(m_message);}
  };
//---
CHello *pstatus;
//+------------------------------------------------------------------+
//| The safe printing of a message using the CHello object pointer     |
//+------------------------------------------------------------------+
void SafePrintStatus(CHello *pobject)
  {
   if(CheckPointer(pobject)==POINTER_INVALID)
      Print(__FUNCTION__," the variable 'object' isn't initialized!");
   else Print(pobject.GetMessage());
  }
//+------------------------------------------------------------------+
//| Printing a message using the CHello object, passed by reference  |
//+------------------------------------------------------------------+
void SafePrintStatus(CHello &pobject)
  {
   DebugBreak();
   SafePrintStatus(GetPointer(pobject));
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- calling a method to show status
   if(GetStop)
      pstatus.GetMessage();
   else
      SafePrintStatus(pstatus);
//--- printing a message if Expert Advisor has been initialized successfully
   Print(__FUNCTION__," The OnInit() function is completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

2つの関数を別の実行(レファレンスを渡すこととオブジェクトポインターを渡すこと)で使う場合、 オーバーロード関数の安全な動作を確保できます。

ついにパラメータとして関数に渡されたオブジェクトの使用方法を学んだので、以下を学ぶ時が来ました。:


いつポインターが必要か?

オブジェクトポインターでオブジェクト作成・破壊のプロセスの柔軟な管理が可能であり、もっと複雑な抽象的オブジェクトの作成が可能です。それはプログラムをもっと柔軟にします。


リンクリスト

しかし、場合によっては異なったやり方によるデータ組織化が要求され、リンクリストはその1つです。リンクリスト CList のクラスは 標準ライブラリにあり、以下が我々自身の例です。リンクリストとは 、各リストアイテムが次と以前のアイテム(存在する場合)に接続されていることを意味します。このようなリンクを組織化するために、リストアイテムのオブジェクトポインター(ListItem)を使用することが便利です。

これのように、リストのアイテムを表すクラスを作りましょう。:

class CListItem
  {
private:
   int               m_ID;
   CListItem        *m_next;
   CListItem        *m_prev;
public:
                    ~CListItem();
   void              setID(int id){m_ID=id;}
   int               getID(){return(m_ID);}
   void              next(CListItem *item){m_next=item;}
   void              prev(CListItem *item){m_prev=item;}
   CListItem*        next(){return(m_next);}
   CListItem*        prev(){return(m_prev);}
  };

リスト自身は別のクラスで組織化されます。:

//+------------------------------------------------------------------+
//| Linked list                                                      |
//+------------------------------------------------------------------+
class CList
  {
private:
   int               m_counter;
   CListItem        *m_first;
public:
                     CList(){m_counter=0;}
                    ~CList();
   void              addItem(CListItem *item);
   int               size(){return(m_counter);}
  };

CListクラスは最初のリストアイテムのポインター m_first を含みます。他のリストアイテム へのアクセスは常に、CListItem()クラスの next () と prev() 関数を通して可能です。CList クラスは2つの興味深い関数があります。一つ目は、新しいアイテムをリストに追加する関数です。

//+------------------------------------------------------------------+
//| Adding of an item to the list                                    |
//+------------------------------------------------------------------+
CList::addItem(CListItem *item)
  {
//--- checking of a pointer, it should be correct
   if(CheckPointer(item)==POINTER_INVALID) return;
//--- increasing the number of list items
   m_counter++;
//--- if there isn't any items in the list
   if(CheckPointer(m_first)!=POINTER_DYNAMIC)
     {
      m_first=item;
     }
   else
     {
      //--- setting for first a pointer to the previous item
      m_first.prev(item);
      //--- saving a pointer of the current first item
      CListItem *p=m_first;
      //--- placing the input item on the place of the first element
      m_first=item;
      //--- for the first item in the list, setting a pointer to the next item 
      m_first.next(p);
     }
  }

リストに追加された各アイテムは最初のアイテムになり、以前の最初のアイテムのポインター が m_next のフィールドに保存されます。このようにして、 最初にリストに追加されたアイテムがリストの最後になります。最後に追加されたアイテムが最初になり、そのポインターが m_first 変数内に保存されます。第二の興味深い関数は ~CList() デストラクタです。オブジェクトが破壊された時に呼ばれ、リストオブジェクトの正しい破壊を確実にするべきです。とてもシンプルにそれをすることができます。:

//+------------------------------------------------------------------+
//| List destructor                                                  |
//+------------------------------------------------------------------+
CList::~CList(void)
  {
   int ID=m_first.getID();
   if(CheckPointer(m_first)==POINTER_DYNAMIC) delete(m_first);
   Print(__FUNCTION__," The first item with ID =",ID," is destroyed");
  }

もし、m_firstがリストアイテムの正しいポインターを含む場合、最初のリストアイテムだけが削除されることが見れます。CListItem() クラスデストラクタが代わって正しいオブジェクト非初期化を生産するため、すべての他のリストアイテムが削除されます。

//+------------------------------------------------------------------+
//| Item destructor                                                  |
//+------------------------------------------------------------------+
CListItem::~CListItem(void)
  {
   if(CheckPointer(m_next)==POINTER_DYNAMIC)
     {
      delete(m_next);
      Print(__FUNCTION__," Removing an item with ID =",m_ID);
     }
   else
      Print(__FUNCTION__," The next item isn't defined for the item with ID=",m_ID);

  }

ポインターの正しさはデストラクタ内で検証できます。指定されていると、ポインター m_next 付きオブジェクトが、delete() 演算子を使って破壊されます。破壊の前、最初のリストアイテムがデストラクタを呼び、それが第二のアイテムを削除し、それが第三 アイテムの削除を起こし、最後のチェーンまでそれが続きます。

リストの作業がスクリプト SampleList.mq5内に表されています。リスト(CListType変数)の宣言は OnStart () 関数内にあります。リストが作成され、自動で初期化されます。リストの記入がリスト内で実行され、最初に、各リストアイテムがnew 演算子を使って動的に作成され、それからリストに追加されます。

void OnStart()
  {
//---
   CList list;
   for(int i=0;i<7;i++)
     {
      CListItem *item=new CListItem;
      item.setID(i);
      list.addItem(item);
     }
     Print("There are ",list.size()," items in the list");
  }

スクリプトを立ち上げると、以下のメッセージが「Experts」 journalに現れます。

2010.03.18 11:22:05 SampleList (EURUSD, H1) CList:: ~ CList first item with ID=6 is destroyed
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem Removing an item with ID = 6
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem Removing an item with ID = 5
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem Removing an item with ID = 4
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem Removing an item with ID = 3
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem Removing an item with ID = 2
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem Removing an item with ID = 1
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem The next item isn't defined for the item with ID=0
2010.03.18 11:22:05 SampleList (EURUSD, H1) There are 7 items in the list

コードを注意深く勉強すると、ID=0付きアイテムが次の以下のアイテムを持たないことに驚かされません。しかし、ポインターの使用がプログラム作成を便利で信頼できるものにする時、1つの理由だけを考えました。第二の理由があり、それは以下として呼ばれます。


多型

同じタイプに属する別のオブジェクトに同じ関数性を実行することがしばしば必要です。例えば、Line, Triangle, Rectangle と Circleのようなシンプルなオブジェクトがあります。異なった外見にもかかわらず、共通の特徴を持ちます。 - 描画されることが可能です。ベースクラスCShapeを作り、幾何学形態の各タイプのdescendants作成に使いましょう。


教育的目的のため、ベースクラスとその継承は最小関数性を持ちます。

//+------------------------------------------------------------------+
//| The base class for a Shape object                                |
//+------------------------------------------------------------------+
class CShape
  {
private:
   int               m_type;
public:
                     CShape(){m_type=0;}
   void              Draw();
   string            getTypeName(){return("Shape");}
  };
//+------------------------------------------------------------------+
//| The class for a Line object                                      |
//+------------------------------------------------------------------+
class CLine:public CShape
  {
private:
   int               m_type;
public:
                     CLine(){m_type=1;}
   void              Draw();
   string            getTypeName(){return("Line");}
  };
//+------------------------------------------------------------------+
//| The class for a Triangle object                                  |
//+------------------------------------------------------------------+
class CTriangle:public CShape
  {
private:
   int               m_type;
public:
                     CTriangle(){m_type=2;}
   void              Draw();
   string            getTypeName(){return("Triangle");}
  };
//+------------------------------------------------------------------+
//| The class for a Rectangle object                                 |
//+------------------------------------------------------------------+
class CRectangle:public CShape
  {
private:
   int               m_type;
public:
                     CRectangle(){m_type=3;}
   void              Draw();
   string            getTypeName(){return("Rectangle");}
  };
//+------------------------------------------------------------------+
//| The class for a Cirlce object                                    |
//+------------------------------------------------------------------+
class CCircle:public CShape
  {
private:
   int               m_type;
public:
                     CCircle(){m_type=4;}
   void              Draw();
   string            getTypeName(){return("Circle");}
  };

親CShapeクラスはその継承に上書きされるDraw() と getTypeName()の2つの関数を含みます。 Draw() 関数は図形を描画することを目的とし、getTypeName() 関数は形状の文字列を戻します。

ベースタイプ CShapeポインター を含み、別クラスのポインター値を指定する*shapes[] 配列を作りましょう。

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- an array of pointers of objects of CShape type
   CShape *shapes[];
//--- resizing of an array 
   ArrayResize(shapes,5);
//--- filling of a pointers array
   shapes[0]=new CShape;
   shapes[1]=new CLine;
   shapes[2]=new CTriangle;
   shapes[3]=new CRectangle;
   shapes[4]=new CCircle;
//--- printing the type of each array element
   for(int i=0;i<5;i++)
     {
      Print(i,shapes[i].getTypeName());
     }
//--- deleting all objects in the array
   for(int i=0;i<5;i++) delete(shapes[i]);
  }

for() サイクル内で、*shapes[] 配列の各エレメントのgetTypeName () 方法を呼びます。派生クラスがgetTypeName() 関数として自身で実行するにもかかわらず、ベースクラスのgetTypeName() 関数がリスト内の各オブジェクトを呼び出す事実に最初はびっくりするかもしれません。

2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 4 Shape
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 3 Shape
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 2 Shape
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 1 Shape
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 0 Shape

この事実の説明は以下です。: *shapes [] 配列はCShapeタイプのポインター の配列として宣言されるため、継承が別の実行を持っていたとしても、各配列オブジェクトはベースクラスの getTypeName() メソッドを呼びます。 実際のオブジェクトタイプ (継承) に対応するgetTypeName() 関数を、プログラム実行の瞬間に呼ぶにはこの関数をvirtual 関数としてベースクラスで宣言することが必要です。

virtual キーワードをgetTypeName() 関数に親CShapeクラスの宣言で追加しましょう。

class CShape
  (
private:
   int m_type;
public:
                     CShape () (m_type = 0;)
   void Draw ();
   virtual string getTypeName () (return ("Shape");)
  )

そしてスクリプトを再度立ち上げます。今、結果は予想と一致しています。:

2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 4 Circle
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 3 Rectangle
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 2 Triangle
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 1 Line
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 0 Shape

そのため、 ベースクラスでの仮想関数の宣言でプログラム実行中に継承の同じ関数を呼び出すことが可能です。これで、各派生クラスに対して完全に整った Draw() 関数を実行できます。

その動作の例は添付 DrawManyObjects.mq5 スクリプトにあり、 チャートのランダムシェイプを示しています。


結論

まとめの時間が来ました。MQL5では、オブジェクトの作成と破壊が自動で実行され、そのためポインターを、本当にそれらが必要で使用方法を理解している場合のみ、使わなければなりません。

しかし、ポインターの使用なしでこれができない場合、 CheckPointer()を使ってポインター正しさを使用前に確認するようにしてください。 - これらのケースのため特別に追加されました。

最後に: MQL5ではポインターはC++で使われるので実際のメモリポインターではありません。そのため、それらを入力パラメーターとしてDLLに渡すべきではありません。

MetaQuotes Software Corp.によりロシア語から翻訳された
元の記事: https://www.mql5.com/ru/articles/36

MetaTrader 5テスターのストラテジーのビジュアル化 MetaTrader 5テスターのストラテジーのビジュアル化

”百聞は一見にしかず”ということわざがあります。パリやヴェニスに関する本を読んだとしても、心のイメージに関して言えばこれらの美しい都市を夜に実際に歩いてみるのと同じ感覚を得ることはできません。ビジュアル化の利点は、私たちの生活のあらゆる部分で見つけることができます。それには市場も含まれます。例えば、チャートのプライスアナリストはインディケーターや、ストラテジーテスティングのビジュアル化ももちろん利用します。この記事ではMetaTrader 5のストラテジーテスターのすべてのヴィジュアライゼーション機能の説明をしていきます。

MQL5クラウドネットワークを使った速度アップ MQL5クラウドネットワークを使った速度アップ

お使いになられているパソコンのコア数はいくつでしょうか?トレーディングストラテジーの最適化のために使えるパソコンは何台あるでしょうか?ここではMQL5クラウドネットワークを使い、マウスをクリックするだけで世界中のコンピューターパワーを利用して計算を早くするための方法を紹介します。"時は金なり"ということわざは、近年より話題となってきました。重要な計算を何十時間もあるいは何日間も待つことはできませこん。

初心者のためのMQL5のカスタムインディケーター 初心者のためのMQL5のカスタムインディケーター

初めての人にはどんな新しいテーマも複雑で学ぶのが難しいように見えます。知っているテーマはシンプルでわかりやすく感じます。しかし、だれもが母国語さえも最初から勉強しなければならないことを単に忘れがちです。自分のトレーディングストラテジーを策定する上で幅広い可能性を提供するMQL5プログラミング言語でもそれは同じです。- 基本的な考えを最もシンプルな例から学びましょう。本記事ではテクニカルインディケーター とMetaTrader 5 クライアントターミナルの相互作用をシンプルなカスタムインディケーター SMAの例を用いて考えます。

定義済みリスクおよびRRレシオに基づく半自動化ドラッグドロップExpert Advisor連携構築 定義済みリスクおよびRRレシオに基づく半自動化ドラッグドロップExpert Advisor連携構築

すべての取引を自動で行うトレーダーもいれば、複数インディケータのアウトプットを基にして自動と手動のミックスで取引を実行するトレーダーもいます。後者のグループの一員として、私はリスクと利益をチャートから直接、動的に評価するための連携ツールが必要でした。本稿は、定義済みの資本リスクおよびR/Rレシオを連携する半自動化Expert Advisorを実装する方法を提供します。EA パネル実行中には、 Expert Advisor リスク、R/R、ロットサイズ パラメータが変更可能です。