English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
MQL5에서 객체 포인터 사용

MQL5에서 객체 포인터 사용

MetaTrader 5 | 1 7월 2021, 10:32
64 0
MetaQuotes
MetaQuotes

소개

MQL5에서는 코드에서 클래스 유형 변수를 추가로 사용하기 위해 고유한 클래스를 작성할 수 있습니다. MQL5의 객체 생성 및 파괴 순서글에서 이미 알고 있듯이 구조 및 클래스는 자동 및 동적의 두 가지 방법으로 생성할 수 있습니다.

객체를 자동으로 생성하려면 클래스 유형 변수를 선언하기만 하면 됩니다. 시스템이 자동으로 생성하고 초기화합니다. 개체를 동적으로 생성하려면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)가 성공적으로 로드되었습니다
14:46:18 GetCriticalError (EURUSD, H1) 초기화 실패
14:46:18 전문가 GetCriticalError (EURUSD, H1) 제거됨

이 예제는 매우 간단하며 오류 잡기가 쉽습니다. 그러나 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를 시작할 수 있습니다.

  1. 심각한 오류 (GetStop = true)
  2. 오류는 없지만 잘못된 포인터에 대한 메시지 (GetStop = false)

기본적으로 Expert Advisor는 성공적으로 실행되고 "Experts" 저널 (Journal)에 다음 메시지가 나타납니다.

GetCriticalError_OnDemand (EURUSD, H1) 15:01:57 PrintStatus '객체 (object)' 변수가 초기화되지 않았습니다!
GetCriticalError_OnDemand (EURUSD, H1) 15:01:57 OnInit 함수 OnInit () 완료

따라서 포인터를 사용하기 전에 검사하면 심각한 오류를 피할 수 있습니다.

함수에서 포인터를 사용하기 전에 항상 포인터정확성을 확인하십시오.

참조로 초기화되지 않은 개체 전달

초기화되지 않은 객체를 함수의 입력 매개 변수로 전달하면 어떻게됩니까? (객체 포인터가 아닌 참조에 의한 객체 자체). 클래스 및 구조와 같은 복잡한 개체는 앰퍼샌드를 사용하여 참조로 전달됩니다. 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 Advisors의 차이점을 볼 수 있습니다. 첫 번째 경우에는 객체 포인터가 함수에 전달되고 두 번째 경우에는 객체 자체가 참조로 전달됩니다. 두 경우 모두 객체와 포인터를 사용하기 전에 함수는 포인터의 정확성을 확인합니다.

Expert Advisors가 동일한 방식으로 작동한다는 의미입니까? 아니요, 그렇지 않습니다! GetStop=false 매개 변수를 사용하여 Expert Advisor를 시작해보면, 다시 심각한 오류가 발생합니다. 사실 객체가 참조로 전달되면 초기화되지 않은 객체가 매개 변수로 전달되기 때문에 함수 호출 단계에서 심각한 오류가 발생합니다. MetaEditor5에서 직접 디버그 모드로 스크립트를 실행하면 F5 버튼을 사용하여 확인할 수 있습니다.

수동 중단점 사용을 피하기 위해 함수 내부에 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-프로그램이 중지됩니다.

안전한 코드를 위해 오버로드된 함수 사용

외부 라이브러리를 사용하는 개발자가 강제로 수행했다면 매우 불편한,입력 개체의 정확성을 확인하십시오. 라이브러리 내에서 필요한 모든 검사를 수행하는 것이 훨씬 낫습니다. 함수 오버로딩을 사용하여 구현할 수 있습니다.

함수 오버로딩을 사용하여 UnsafePrintStatus() 메소드를 구현하고이 함수의 두 가지 버전을 작성해 봅시다. 첫 번째는 객체 자체 대신 전달된 객체 포인터를 사용하는 것이고 두 번째는 참조에 의한 객체 전달을 사용하는 것입니다. 두 함수 모두 "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()
  {
//---

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

구현이 다른 두 개의 함수를 사용하는 경우 (참조를 전달하고 개체 포인터를 전달하여) 오버로드된 함수의 안전한 작업을 보장 할 수 있습니다.

마지막으로, 매개 변수로 함수에 전달된 객체를 사용하는 방법을 배웠습니다. 이제 배울 차례입니다.

언제 포인터가 필요합니까?

개체 포인터를 사용하면 개체 생성 및 삭제 프로세스를 유연하게 관리할 수 ​​있으며 더 복잡한 추상 개체를 만들 수 있습니다. 프로그램을 더 유연하게 만듭니다.

연결된 목록

하지만 경우에 따라 다른 방식의 데이터 구성이 필요한 경우 연결 목록이 그중 하나입니다. 연결 목록 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 클래스에는 두 가지 흥미로운 기능이 있습니다. 첫 번째는 목록에 새 항목을 추가하는 기능입니다.

//+------------------------------------------------------------------+
//| 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</ i0>에 목록 항목의 올바른 포인터가 포함되어 있으면 첫 번째 목록 항목만 제거된다는 것을 알 수 있습니다. CListItem() 클래스 소멸자가 차례로 올바른 객체 초기화를 생성하기 때문에 다른 모든 목록 항목은 애발랜치 (avalanche)가 제거됩니다.

//+------------------------------------------------------------------+
//| 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"저널에 다음 메시지가 표시됩니다.

2010.03.18 11:22:05 SampleList (EURUSD, H1) CList:: ~ CList ID=6 인 첫번째 항목이 소멸됩니다.
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem = 6 인 항목 제거
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem = 5 인 항목 제거
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem = 4 인 항목 제거
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItemID가 3 인 항목 제거
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem ID = 2 인 항목 제거
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem ID = 1 인 항목 제거
2010.03.18 11:22:05 SampleList (EURUSD, H1) CListItem:: ~ CListItem 다음 항목은 ID = 0 인 항목에 대해 정의되지 않았습니다.
2010.03.18 11:22:05 SampleList (EURUSD, H1) 총 7 개 항목이 있습니다.

코드를 꼼꼼히 살펴 보셨다면 ID=0 인 항목에 다음 항목이 없는 것도 놀라운 일이 아닙니다. 그러나 포인터를 사용하여 프로그램을 편리하고 읽기 쉽게 작성하는 이유는 한 가지만 고려했습니다. 두 번째 이유가 있습니다.

다형성

동일한 유형에 속하는 다른 객체에 대해 동일한 기능을 구현해야하는 경우가 많습니다. 예를 들어 Line, Triangle, Rectangle 및 Circle과 같은 간단한 개체가 있습니다. 다르게 보이지만 공통된 특징이 있음에도 불구하고 그릴 수 있습니다. 기본 클래스 CShape를 만들고 각 유형의 기하학적 모양에 대한 하위 항목을 만드는 데 사용합니다.


교육 목적을 위해 기본 클래스와 그 하위 클래스에는 최소한의 기능이 있습니다.

//+------------------------------------------------------------------+
//| 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()에서 재정의되는 두 개의 함수가 포함되어 있습니다. 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 모양
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 3 모양
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 2 모양
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 1 모양
2010.03.18 14:06:18 DemoPolymorphism (EURUSD, H1) 0 모양

이 사실에 대한 설명은 다음과 같습니다. *shapes [] 배열은 CShape 유형의 포인터 배열로 선언되므로 각 배열 객체는 기본 클래스의 getTypeName() 메소드를 호출합니다. 하위 항목에 다른 구현이 있더라도. 프로그램 실행시 실제 객체 유형 (하위)에 해당하는 getTypeName() 함수를 호출하려면 기본 클래스에서 이 함수를 가상 함수로 선언해야 합니다.

부모 CShape 클래스의 선언에서 getTypeName() 함수에 virtual 키워드를 추가해보겠습니다.

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 줄
2010.03.18 15:01:11 DemoPolymorphism (EURUSD, H1) 0 모양

따라서 기본 클래스에서 가상 함수를 선언하면 프로그램 실행 중에 자손과 동일한 함수를 호출할 수 있습니다. 이제 각 파생 클래스에 대해 모든 기능을 갖춘 Draw() 함수를 구현할 수 있습니다.

작업의 예는 첨부된 DrawManyObjects.mq5 스크립트에서 찾을 수 있습니다. 이 스크립트는 차트에 임의의 모양을 표시합니다.

결론

그래서 요약할 시간입니다. MQL5에서는 객체 생성 및 삭제가 자동으로 수행되므로 포인터가 실제로 필요하고 작업 방법을 이해하는 경우에만 포인터를 사용해야 합니다.

그러나 포인터를 사용하지 않고 수행할 수 없는 경우에는 CheckPointer()를 사용하여 사용하기 전에 포인터 정확성을 확인하십시오. 이러한 경우에 특별히 추가되었습니다.

마지막으로, MQL5에서 포인터는 C++에서 사용되는 실제 메모리 포인터가 아니므로 입력 매개 변수로 DLL에 전달해서는 안됩니다.

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/36

MQL5 for Newbies의 맞춤 인디케이터 MQL5 for Newbies의 맞춤 인디케이터
새로운 주제는 초보자에게 복잡하고 배우기 어려운 것 같습니다. 우리가 알고있는 주제는 우리에게 매우 간단하고 명확해 보입니다. 그러나 우리는 모든 사람이 처음부터 무언가를 심지어 우리의 모국어로 공부해야 한다는 걸 기억하지 못하는 것 같습니다. 자신의 거래 전략을 개발할 수있는 광범위한 가능성을 제공하는 MQL5 프로그래밍 언어도 마찬가지입니다. 기본 개념과 가장 간단한 예를 통해 학습을 시작할 수 있습니다. 기술 인디케이터와 MetaTrader 5 클라이언트 터미널의 상호 작용은 간단한 사용자 지정 인디케이터 SMA의 예에 대한 이 글에서 고려됩니다.
MQL5 소개: 간단한 전문가 자문 및 사용자 지정 지표 작성 방법 MQL5 소개: 간단한 전문가 자문 및 사용자 지정 지표 작성 방법
MetaTrader 5 클라이언트 터미널에 포함된 MQL5(MetaQuotes Programming Language 5)는 MQL4에 비해 많은 새로운 가능성과 더 높은 성능을 가지고 있다. 이 글은 당신이 이 새로운 프로그래밍 언어에 익숙해지도록 도와줄 것이다. 이 문서에는 전문가 자문 및 사용자 지정 지표 작성 방법의 간단한 예가 나와 있습니다. 저희는 또한 이러한 예를 이해하는 데 필요한 MQL5 언어의 몇 가지 세부 사항을 고려할 것이다.
Expert Advisor에서 OnTrade() 함수를 이용한 거래 이벤트 처리 Expert Advisor에서 OnTrade() 함수를 이용한 거래 이벤트 처리
MQL5는 다양한 유형의 이벤트 (타이머 이벤트, 거래 이벤트, 맞춤 이벤트 등) 작업을 포함하여 많은 혁신을 제공했습니다. 이벤트 처리 기능을 통해 자동 및 반자동 거래를 위한 완전히 새로운 유형의 프로그램을 만들 수 있습니다. 이 기사에서는 거래 이벤트를 고려하고 거래 이벤트를 처리할 OnTrade() 함수에 대한 코드를 작성합니다.
초보자를 위한 MQL5 : Expert Adviser의 기술 지표 사용 가이드 초보자를 위한 MQL5 : Expert Adviser의 기술 지표 사용 가이드
Expert Advisor에서 내장 또는 사용자 지정 인디케이터의 값을 얻으려면 먼저 해당 기능을 사용하여 핸들을 만들어야 합니다. 이 글에서의 예는 자신의 프로그램을 만드는 동안 이 또는 해당 기술 지표를 사용하는 방법을 보여줍니다. 이 글에서는 MQL5 언어로 빌드된 인디케이터에 대해 설명합니다. 트레이딩 전략 개발에 대한 경험이 많지 않은 사람들을 위해 제공되는 기능 라이브러리를 사용하여 지표로 작업하는 간단하고 명확한 방법을 제공합니다.