English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
객체 지향 프로그래밍의 기초

객체 지향 프로그래밍의 기초

MetaTrader 5 | 5 7월 2021, 10:56
105 0
Dmitry Fedoseev
Dmitry Fedoseev

 

소개

객체 지향 프로그래밍 (OOP) 학습을 시작하려는 사람은 누구나 다형성, 캡슐화, 오버로드상속과 같은 단어를 처음 접했다고 가정할 수 있습니다. 누군가 미리 만들어진 클래스를보고 그 다형성이나 캡슐화가 실제로 어디에 있는지 알아내려고 했을 수도 있습니다. 대부분 이것은 OOP 학습 과정의 끝일 수 있습니다.

사실 모든 것이 보기보다 훨씬 간단합니다. OOP를 사용하기 위해 이 단어가 무엇을 의미하는지 알 필요가 없습니다. - OOP 기능만 사용할 수 있으며 이름이 무엇인지 알지 못합니다. 그래도 이 글을 읽을 모든 분들이 OOP 사용법을 배우는 것뿐만 아니라 이 단어들의 의미도 분명히 해주셨으면 합니다.

 

함수 라이브러리 생성

OOP의 첫 번째이자 가장 간단한 응용 프로그램은 자주 사용하는 함수의 라이브러리를 만드는 것입니다. 물론 이러한 함수를 포함 파일 (mqh)에 간단히 저장할 수 있습니다. 함수가 필요한 경우 파일을 포함하고 이 함수를 호출하면 됩니다. 그러나 충분히 오래 프로그래밍하면 엄청난 양의 함수를 수집 할 수 있으므로 이름과 목적을 기억하기 어려울 수 있습니다.

여러 파일에서 함수를 수집하여 목적에 따라 범주로 분할할 수 있습니다. 예를 들어 배열 작업 기능, 문자열 작업 기능, 순서 계산 기능 등이 있습니다. 마지막 문장에서 "category"라는 단어는 "classes"라는 단어로 대체 될 수 있습니다. 의미는 동일하게 유지되지만 객체 지향 프로그래밍이라는 주제에 더 가까이 다가갈 것입니다.

따라서 함수는 배열로 작업할 함수 클래스, 문자열로 작업할 함수 클래스, 순서를 세는 함수 클래스 등으로 나눌 수 있습니다. "클래스"라는 단어는 기본 개념이기 때문에 OOP의 주제에 더 가까워집니다. 다양한 참고서, 사전 및 백과 사전 (예 : Wikipedia)에서 "프로그래밍 클래스"가 무엇인지 검색 할 수 있습니다.

객체 지향 프로그래밍에서 클래스는 자체 인스턴스를 생성하기위한 청사진으로 사용되는 구조입니다.

아마도 첫인상은 "다형성", "캡슐화"등의 단어와 거의 같을 것입니다. 이 순간 '클래스'라는 이름은 함수와 변수의 집합을 의미합니다. 클래스를 사용하여 라이브러리를 만드는 경우 - 처리된 데이터 유형 또는 처리 된 객체 유형 (배열, 문자열, 순서)별로 그룹화 된 함수 및 변수 집합입니다.

 

프로그램의 프로그램

포럼에 대해 유사한 질문이 많이있었습니다 (그리고 앞으로도 있을 것입니다) - Expert Advisor로부터 스크립트를 호출하는 방법은 무엇입니까? 타사 도구를 사용하지 않고 스크립트 코드를 Expert Advisor 코드에 배치하여이 작업을 수행합니다. 사실 어려운 작업은 아니지만 스크립트는 EA와 동일한 이름의 변수와 함수를 사용할 수 있으므로 스크립트 코드를 조정해야 합니다. 변경 사항은 복잡하지는 않지만 볼륨면에서 상당 할 것입니다.

이 스크립트를 별도의 독립 프로그램으로 부르면 좋을 것입니다! 스크립트를 클래스로 프로그래밍 한 다음이 클래스를 사용하면 가능합니다. 작업량은 몇 줄의 코드로만 증가합니다. 이 경우 클래스는 처리된 데이터의 유형이 아니라 목적에 따라 함수를 결합합니다. 예: 보류중인 주문을 삭제하는 클래스, 포지션을 열거 나 주문하는 클래스, 그래픽 개체로 작업 할 클래스 등.

클래스의 중요한 특징은 그것이 위치한 공간과 구별된다는 것입니다. 이 클래스는 운영 체제에서 실행되는 프로그램과 같습니다. 여러 프로그램이 동시에 실행될 수 있지만 서로 독립적으로 실행될 수 있습니다. 따라서 클래스는 위치하는 공간과 구별되므로 "프로그램의 프로그램"이라고 할 수 있습니다.

 

수업의 모양과 느낌

클래스 생성은 class라는 단어로 시작하고 클래스 이름이 뒤 따르고 클래스의 전체 코드가 중괄호 안에 배치됩니다.

class CName 
  {
   // Here is the entire code of the class
  };
주의! 닫는 중괄호 뒤에 세미콜론을 넣는 것을 잊지 마십시오.

 

표시 및 숨김 (캡슐화)

어떤 프로그램을 수강하면 다양한 기능이 포함되어 있음을 알고 있습니다. 이러한 기능은 주 및 보조의 두 가지 유형으로 나눌 수 있습니다. 주요 기능은 프로그램이 실제로 구성되는 기능입니다. 이러한 기능에는 사용자가 알 필요가 없는 다른 많은 기능이 필요할 수 있습니다. 예를 들어, 클라이언트 터미널에서 포지션 트레이더를 열려면 새 주문 대화 상자를 열고 손절매 및 이익 실현의 볼륨, 값을 입력 한 다음 "매수"또는 "매도"를 클릭해야 합니다.

그러나 버튼을 클릭하고 포지션을 여는 것 사이에 실제로 일어나는 일은 터미널 개발자만이 확실히 알 수 있습니다. 터미널이 볼륨 위치 확인, 손절매 및 이익 실현 값 확인, 네트워크 연결 확인 등 많은 작업을 한다고 가정할 수 있습니다. 많은 절차가 숨겨져 있거나, 즉 캡슐화 됩니다. 마찬가지로 클래스에서 코드를 조각 (함수 및 변수)으로 분할 할 수 있습니다. 일부는 클래스를 사용할 때 사용할 수 있고 일부는 숨겨집니다.

캡슐화 수준은 private, protected public 키워드를 사용하여 정의됩니다. 보호비공개의 차이점은 잠시 후에 고려되지만 먼저 비공개공개 키워드에 대해 살펴 보겠습니다. 따라서 간단한 클래스 템플릿은 다음과 같은 형식을 취합니다.

class CName 
  {
private:
   // Variables and functions available only inside the class
public:
   // Variables and functions available outside the class
  };
OOP를 활용하기에 충분합니다. Expert Advisor (스크립트 또는 인디케이터)에서 직접 코드를 작성하는 대신 먼저 클래스를 만든 다음이 클래스의 모든 내용을 작성합니다. 다음으로 실제 예에서 privatepublic 섹션의 차이점을 고려합니다.

 

라이브러리 생성 예

위에 제시된 클래스 템플릿을 사용하여 함수 라이브러리를 만들 수 있습니다. 배열로 작업할 클래스를 만들어 보겠습니다. 배열을 사용할 때 발생할 수있는 가장 일반적인 작업은 배열에 새 요소를 추가하고 지정된 값을 가진 요소가 배열에 존재하지 않는 경우 새 요소를 추가하는 것입니다.

배열에 요소를 추가하는 함수의 이름을 AddToEnd()로 지정하고 배열에 고유 요소를 추가하는 함수를 AddToEndIfNotExists()로 지정합니다. AddToEndIfNotExists() 함수에서 먼저 추가된 요소가 이미 배열에 존재하는지 확인하고 그렇지 않은 경우 AddToEnd() 함수를 사용합니다. 요소가 이미 배열에 존재하는지 확인하는 함수는 보조로 간주되므로 private 섹션과 다른 모든 함수 - public 섹션에 배치합니다. 결과적으로 다음과 같은 클래스를 얻게됩니다.

class CLibArray 
  {
private:
   // Check if an element with required value exists in array
   int Find(int &aArray[],int aValue) 
     {
      for(int i=0; i<ArraySize(aArray); i++) 
        {
         if(aArray[i]==aValue) 
           {
            return(i); // Element exists, return index of element
           }
        }
      return(-1);  // No such element, return -1
     }
public:
   // Add to end of array
   void AddToEnd(int &aArray[],int aValue) 
     {
      int m_size=ArraySize(aArray);
      ArrayResize(aArray,m_size+1);
      aArray[m_size]=aValue;
     }
   // Add to end of array if there is no such value in array already
   void AddToEndIfNotExistss(int &aArray[],int aValue) 
     {
      if(Find(aArray,aValue)==-1) 
        {
         AddToEnd(aArray,aValue);
        }
     }
  };
 

로딩 클래스

클래스를 사용하려면 로드 해야 합니다. 클래스가 별도의 파일에 있는 경우 이 파일을 포함해야 합니다.

#include <OOP_CLibArray_1.mqh>

이 클래스를 로드합니다. 클래스 로딩은 변수 선언과 유사합니다.

CLibArray ar;

먼저 클래스의 이름이 나오고 그 다음이 인스턴스를 참조하는 포인터의 이름이 옵니다. 로드 후 클래스는 객체가 됩니다. 객체의 기능을 사용하려면 포인터 이름, 점 및 함수 이름을 입력하십시오. 점을 입력하면 클래스 함수 드롭 다운 목록이 열립니다 (그림 1).

그림 1. 기능 목록
그림 1. 기능 목록

드롭 다운 목록 덕분에 함수 이름을 기억할 필요가 없습니다. 이름 목록을 탐색하고 함수의 용도를 기억할 수 있습니다. 이것은 단순히 파일에서 함수를 수집하는 것과 달리 클래스를 사용하여 라이브러리를 만드는 가장 큰 장점입니다.

함수 수집의 경우 함수 이름의 첫 글자 몇 개를 입력하면 드롭 다운 목록에 포함된 모든 라이브러리의 모든 함수가 표시되고 클래스를 사용하면 지정된 클래스와 관련된 함수만 표시됩니다. 또한 Find() 함수는 나열되지 않습니다. 이것이 private 섹션과 public 섹션의 차이점입니다. 이 함수는 private 섹션에 작성되어 있으므로 사용할 수 없습니다.

 

다양한 데이터 유형에 대한 범용 라이브러리 만들기 (오버로드)

이 시점에서 라이브러리에는 int 유형의 배열에서만 작동하는 함수가 포함되어 있습니다. int 유형 배열 외에도 다음 유형의 배열에 라이브러리 함수를 적용해야 할 수 있습니다. uint, long, ulong 등 다른 데이터 유형의 배열의 경우 자체 함수를 작성해야 합니다. 그러나 이러한 함수에 다른 이름을 지정할 필요는 없습니다. 전달된 매개 변수 유형 또는 매개 변수 집합 (이 예에서는 매개 변수 유형에 따라)에 따라 올바른 함수가 자동으로 선택됩니다. long 유형의 배열로 작업하는 함수로 클래스를 보완 해 보겠습니다.

class CLibArray 
  {
private:
   // Для int. Check if an element with required value exists in array
   int Find(int &aArray[],int aValue)
     {
      for(int i=0; i<ArraySize(aArray); i++) 
        {
         if(aArray[i]==aValue) 
           {
            return(i); // Element exists, return index of element
           }
        }
      return(-1); // No such element, return -1
     }
   // For long. Check if an element with required value exists in array
   int Find(long &aArray[],long aValue) 
     {
      for(int i=0; i<ArraySize(aArray); i++) 
        {
         if(aArray[i]==aValue) 
           {
            return(i); // Element exists, return index of element
           }
        }
      return(-1); // No such element, return -1
     }
public:
   // For int. Add to end of array
   void AddToEnd(int &aArray[],int aValue) 
     {
      int m_size=ArraySize(aArray);
      ArrayResize(aArray,m_size+1);
      aArray[m_size]=aValue;
     }
   // For long. Add to end of array
   void AddToEnd(long &aArray[],long aValue) 
     {
      int m_size=ArraySize(aArray);
      ArrayResize(aArray,m_size+1);
      aArray[m_size]=aValue;
     }
   // For int. Add to end of array if there is no such value in array already
   void AddToEndIfNotExistss(int &aArray[],int aValue) 
     {
      if(Find(aArray,aValue)==-1) 
        {
         AddToEnd(aArray,aValue);
        }
     }
   // For long. Add to end of array if there is no such value in array already
   void AddToEndIfNotExistss(long &aArray[],long aValue) 
     {
      if(Find(aArray,aValue)==-1) 
        {
         AddToEnd(aArray,aValue);
        }
     }
  };
이제 같은 이름을 사용하면 다른 기능이 있습니다. 하나의 이름이 둘 이상의 기능, 즉 오버로드로 로드되므로 이러한 함수를 오버로드라고 합니다.

이 글 에 첨부된 OOP_CLibArray_1.mqh 파일에서 이 예제를 찾을 수 있습니다.

 

또 다른 클래스 표기법

위의 예에서 모든 함수는 클래스 내부에 작성되었습니다. 많은 기능이 있고 각각의 기능이 상당히 방대하다면 그러한 표기법이 그다지 편하지 않을 수 있습니다. 이러한 경우 클래스 외부에 함수를 배치할 수 있습니다. 클래스 내부에는 매개 변수가 있는 함수의 이름만 작성하고 함수는 클래스 외부에서 완전히 설명됩니다. 게다가 함수가 특정 클래스에 속함을 나타내야 합니다. 먼저 클래스 이름을 작성한 다음 두 개의 콜론과 함수 이름을 입력합니다.

class CLibArray 
  {
private:
   int               Find(int  &aArray[],int  aValue);
   int               Find(long &aArray[],long aValue);
public:
   void              AddToEnd(int  &aArray[],int  aValue);
   void              AddToEnd(long &aArray[],long aValue);
   void              AddToEndIfNotExistss(int  &aArray[],int  aValue);
   void              AddToEndIfNotExistss(long &aArray[],long aValue);
  };
//---
int CLibArray::Find(int &aArray[],int aValue) 
  {
   for(int i=0; i<ArraySize(aArray); i++) 
     {
      if(aArray[i]==aValue) 
        {
         return(i);
        }
     }
   return(-1);
  }
//---
int CLibArray::Find(long &aArray[],long aValue) 
  {
   for(int i=0; i<ArraySize(aArray); i++) 
     {
      if(aArray[i]==aValue) 
        {
         return(i);
        }
     }
   return(-1);
  }
//---
void CLibArray::AddToEnd(int &aArray[],int aValue) 
  {
   int m_size=ArraySize(aArray);
   ArrayResize(aArray,m_size+1);
   aArray[m_size]=aValue;
  }
//---
void CLibArray::AddToEnd(long &aArray[],long aValue) 
  {
   int m_size=ArraySize(aArray);
   ArrayResize(aArray,m_size+1);
   aArray[m_size]=aValue;
  }
//---
void CLibArray::AddToEndIfNotExistss(int &aArray[],int aValue) 
  {
   if(Find(aArray,aValue)==-1) 
     {
      AddToEnd(aArray,aValue);
     }
  }
//---
void CLibArray::AddToEndIfNotExistss(long &aArray[],long aValue) 
  {
   if(Find(aArray,aValue)==-1) 
     {
      AddToEnd(aArray,aValue);
     }
  }

이러한 표기법을 사용하면 클래스 구성의 전체 그림을 얻고 필요한 경우 개별 함수를 자세히 살펴볼 수 있습니다.

이 글 에 첨부된 OOP_CLibArray_2.mqh 파일에서 이 예제를 찾을 수 있습니다.

 

클래스에서 변수 선언

앞서 언급한 예를 계속 살펴 보겠습니다. 파일에서 직접 코딩하는 것과 클래스 내부에서 코딩하는 것에는 한 가지 차이점이 있습니다. 파일에서 직접 선언할 때 값으로 변수를 할당할 수 있습니다.

int Var = 123;

클래스에서 변수를 선언하면 이 작업을 수행할 수 없습니다. 클래스의 일부 기능을 실행할 때 값을 할당해야 합니다. 따라서 먼저 매개 변수를 클래스에 전달해야 합니다 (즉, 작업할 클래스 준비). 이 함수의 이름을 Init()로 지정합니다.

실용적인 예를 들어보십시오.

 

스크립트를 클래스로 변환하는 예

보류중인 주문을 삭제하는 스크립트가 있다고 가정합니다 (첨부된 OOP_sDeleteOrders_1.mq5 파일 참조).

// Include file to use the CTrade class from standard delivery
#include <Trade/Trade.mqh>

// External parameters

// Select symbol. true  - delete orders for all symbols, 
//                false - only for symbol of chart, where the script is running
input bool AllSymbol=false;

// Select types of orders to delete
input bool BuyStop       = false;
input bool SellStop      = false;
input bool BuyLimit      = false;
input bool SellLimit     = false;
input bool BuyStopLimit  = false;
input bool SellStopLimit = false;

// Load the CTrade class
CTrade Trade;
//---
void OnStart()
  {
// Variable to check function result
   bool Ret=true;
// Loop by all orders in terminal
   for(int i=0; i<OrdersTotal(); i++)
     {
      ulong Ticket=OrderGetTicket(i); // Select order and get its ticket
                                      // Successfully selected
      if(Ticket>0)
        {
         long Type=OrderGetInteger(ORDER_TYPE);
         // Check order type
         if(Type == ORDER_TYPE_BUY_STOP && !BuyStop) continue;
         if(Type == ORDER_TYPE_SELL_STOP && !SellStop) continue;
         if(Type == ORDER_TYPE_BUY_LIMIT && !BuyLimit) continue;
         if(Type == ORDER_TYPE_SELL_LIMIT && !SellLimit) continue;
         if(Type == ORDER_TYPE_BUY_STOP_LIMIT && !BuyStopLimit) continue;
         if(Type == ORDER_TYPE_SELL_STOP_LIMIT && !SellStopLimit) continue;
         // Check symbol
         if(!AllSymbol && Symbol()!=OrderGetString(ORDER_SYMBOL)) continue;
         // Delete
         if(!Trade.OrderDelete(Ticket))
           {
            Ret=false; // Failed to delete
           }
        }
      // Failed to select order, unknown result,
      // function ended up with error
      else
        {
         Ret=false;
         Print("Error selecting order");
        }
     }

   if(Ret)
     {
      Alert("Script ended successfully");
     }
   else    
     {
      Alert("Script ended up with error, see details. in Journal");
     }
  }

스크립트에는 다양한 유형의 주문을 활성화하고 주문을 삭제할 기호 (스크립트가 실행중인 차트의 모든 기호 또는 기호)를 선택할 수있는 외부 매개 변수가 있습니다.

이 스크립트를 COrderDelete라는 클래스로 변환하십시오. private 섹션에서 스크립트에 선언된 것과 동일한 변수를 외부 매개 변수로 선언하고 변수 이름에 "m _"(단어 "멤버", 즉 클래스의 멤버) 접두사를 선언합니다. 접두사 추가는 필수는 아니지만 변수를 쉽게 구분할 수 있어 매우 편리합니다. 따라서 저희는 클래스 공간에 의해 제한된 변수를 다루고 있음을 확실히 알 수 있습니다. 또한 변수 선언이 전역 범위에서 선언된 변수를 숨긴다는 컴파일러 경고를 받지 않습니다.

전역 범위, 클래스 정의, 함수 본문에서 동일한 변수 이름을 사용하는 것은 오류가 아니지만 프로그램을 이해하기 어렵게 만들기 때문에 이러한 경우 컴파일러에서 경고를 표시합니다. 값이 있는 변수를 할당하려면 이러한 변수 (및 스크립트의 외부 매개 변수)에 해당하는 매개 변수를 사용하여 Init() 함수를 작성하십시오. 이 클래스를 사용하는 경우 먼저 Init() 함수를 호출하고 외부 매개 변수를 전달해야 합니다. 나머지 스크립트 코드는 변경되지 않습니다. 유일한 예외 - 외부 매개 변수를 직접 사용하는 대신 클래스 내에서 선언 된 변수를 사용해야 합니다.

따라서 다음과 같은 클래스를 얻습니다.

#include <Trade/Trade.mqh> 

class COrderDelete 
  {

private:
   // Variables for parameters
   bool              m_AllSymbol;
   bool              m_BuyStop;
   bool              m_SellStop;
   bool              m_BuyLimit;
   bool              m_SellLimit;
   bool              m_BuyStopLimit;
   bool              m_SellStopLimit;
   // Load the CTrade class
   CTrade            m_Trade;
public:
   // Function to set parameters
   void Init(bool aAllSymbol,bool aBuyStop,bool aSellStop,bool aBuyLimit,bool aSellLimit,bool aBuyStopLimit,bool aSellStopLimit) 
     {
      // Set parameters
      m_AllSymbol    =aAllSymbol;
      m_BuyStop      =aBuyStop;
      m_SellStop     =aSellStop;
      m_BuyLimit     =aBuyLimit;
      m_SellLimit    =aSellLimit;
      m_BuyStopLimit =aBuyStopLimit;
      m_SellStopLimit=aSellStopLimit;
     }
   Main function to delete orders
   bool Delete() 
     {
      // Variable to check function result
      bool m_Ret=true;
      // Loop by all orders in terminal
      for(int i=0; i<OrdersTotal(); i++) 
        {
         // Select order and get its ticket
         ulong m_Ticket=OrderGetTicket(i);
         // Successfully selected
         if(m_Ticket>0) 
           {
            long m_Type=OrderGetInteger(ORDER_TYPE);
            // Check order type
            if(m_Type == ORDER_TYPE_BUY_STOP && !m_BuyStop) continue;
            if(m_Type == ORDER_TYPE_SELL_STOP && !m_SellStop) continue;
            if(m_Type == ORDER_TYPE_BUY_LIMIT && !m_BuyLimit) continue;
            if(m_Type == ORDER_TYPE_SELL_LIMIT && !m_SellLimit) continue;
            if(m_Type == ORDER_TYPE_BUY_STOP_LIMIT && !m_BuyStopLimit) continue;
            if(m_Type == ORDER_TYPE_SELL_STOP_LIMIT && !m_SellStopLimit) continue;
            // Check symbol/s61>
            if(!m_AllSymbol && Symbol()!=OrderGetString(ORDER_SYMBOL)) continue;
            // Delete
            if(!m_Trade.OrderDelete(m_Ticket)) 
              {
               m_Ret=false; // Failed to delete
              }
           }
         // Failed to select order, unknown result,
         // function ended up with error
         else 
           {
            m_Ret=false;
            Print("Error selecting order");
           }
        }
      // Return function result
      return(m_Ret);
     }
  };
이 문서에 첨부된 OOP_CDeleteOrder_1.mqh 파일에서 이 클래스의 예를 찾을 수 있습니다. 이 클래스를 사용하는 스크립트는 최소한으로 축소됩니다 (외부 매개 변수, 클래스로드, Init() 및 Delete() 메소드 호출).
// External parameters

// Select symbol. true  - delete orders for all symbols, 
//                false - only for symbol of chart, where the script is running
input bool AllSymbol=false;

// Select types of orders to delete
input bool BuyStop       = false;
input bool SellStop      = false;
input bool BuyLimit      = false;
input bool SellLimit     = false;
input bool BuyStopLimit  = false;
input bool SellStopLimit = false;

// Include file with class
#include <OOP_CDeleteOrder_1.mqh> 

// Load class
COrderDelete od;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart() 
  {
// Pass external parameters to the class
   od.Init(AllSymbol,BuyStop,SellStop,BuyLimit,SellLimit,BuyStopLimit,SellStopLimit);
// Delete orders
   bool Ret=od.Delete();
// Process result of deleting
   if(Ret) 
     { 
       Alert("Script ended successfully"); 
     }
   else    
     { 
       Alert("Script ended up with error, see details in Journal"); 
     }
  }

이 문서에 첨부된 OOP_sDeleteOrders_2.mq5 파일에서 이 스크립트의 예를 찾을 수 있습니다. 대부분의 스크립트는 Delete() 함수의 결과를 처리하므로 스크립트 결과를 알립니다.

이제 스크립트의 모든 기본 기능이 별도의 파일에있는 클래스로 설계되었으므로 다른 프로그램 (Expert Advisor 또는 Script)에서이 클래스를 사용할 수 있습니다. 즉, Expert Advisor에서 이 스크립트를 호출할 수 있습니다.

 

약간의 자동화 (생성자 및 소멸자)

프로그램 운영은 프로그램 실행, 작업 프로세스, 작업 완료의 세 단계로 나눌 수 있습니다. 이 분리의 중요성은 분명합니다. 프로그램이 시작될 때 자체 준비 (예 : 작업 할 매개 변수로드 및 설정), 프로그램 종료시 "정리"해야 합니다 (예 : 차트에서 그래픽 개체 제거).

이러한 단계를 분리하기 위해 Expert Advisors 및 인디케이터에는 OnInit() (시작시 실행) 및 OnDeinit() (종료시 실행)과 같은 특수 기능이 있습니다. 클래스에는 비슷한 기능이 있습니다. 클래스가 로드되고 언로드 될 때 자동으로 실행되는 함수를 추가할 수 있습니다. 이러한 함수를 생성자 및 소멸자라고 합니다. 클래스에 생성자를 추가한다는 것은 클래스 이름과 정확히 같은 이름으로 함수를 추가하는 것을 의미합니다. 소멸자를 추가하려면 - 생성자와 동일하게 모든 작업을 수행하지만 함수 이름은 물결표 "~"로 시작합니다.

생성자와 소멸자를 보여주는 스크립트:

// Class
class CName 
  {
public:
   // Constructor
                     CName() { Alert("Constructor"); }
   // Destructor
                    ~CName() { Alert("Destructor"); }

   void Sleep() { Sleep(3000); }
  };

// Load class
CName cname;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart() 
  {
// Pause
   cname.Sleep();
  }

이 클래스에는 실제로 3 초 동안 일시 중지하는 Sleep() 함수가 하나만 있습니다. 스크립트를 실행하면 "Constructor"메시지가 있는 경고 창이 나타나고 3 초 동안 일시 중지 된 후 "소멸자" 메시지가 있는 경고 창이 표시됩니다. 이것은 CName() 및 ~ CName() 함수가 명시 적으로 호출되지 않았음에도 불구하고 그렇습니다.

이 예는 이 글 에 첨부 된 OOP_sConstDestr_1.mq5 파일에서 찾을 수 있습니다.

 

생성자에게 매개 변수 전달

스크립트를 클래스로 변환한 예제에서 코드의 양을 한 줄 줄일 수 있습니다. Init() 함수를 호출하지 않아도 됩니다. 클래스를 로드할 때 매개 변수를 생성자에 전달할 수 있습니다. 클래스에 생성자를 추가합니다.

COrderDelete(bool aAllSymbol     = false,
             bool aBuyStop       = false,
             bool aSellStop      = false,
             bool aBuyLimit      = false,
             bool aSellLimit     = false,
             bool aBuyStopLimit  = false,
             bool aSellStopLimit=false) 
  {
   Init(aAllSymbol,aBuyStop,aSellStop,aBuyLimit,aSellLimit,aBuyStopLimit,aSellStopLimit);
  }

Init() 함수는 그대로 유지되지만 생성자에서 호출됩니다. 생성자의 모든 매개 변수는 선택 사항이므로 이전과 같이 클래스를 사용할 수 있습니다. 매개 변수없이 클래스를 로드하고 Init() 함수를 호출합니다.

생성자를 만든 후 이 클래스를 사용하는 다른 방법이 있습니다. Init() 함수를 호출할 필요없이 변수를 많은 수 있습니다.

COrderDelete od(AllSymbol,BuyStop,SellStop,BuyLimit,SellLimit,BuyStopLimit,SellStopLimit);

Init() 함수는 클래스 재 초기화를 허용하기 위해 public 섹션에 남아 있습니다. 프로그램 (Expert Advisor)을 사용할 때 한 경우에는 Stop 주문만 제거해야 할 수도 있고 다른 경우에는 Limit 문 만 제거해야 할 수도 있습니다. 이렇게 하려면 다른 매개 변수를 사용하여 Init() 함수를 호출하여 Delete() 함수가 다른 주문 세트를 삭제하도록 할 수 있습니다.

이 문서에 첨부된 OOP_CDeleteOrder_2.mqh 및 OOP_sDeleteOrders_3.mq5 파일에서 이 예제를 찾을 수 있습니다.

 

클래스의 여러 인스턴스 사용

이전 섹션에서 언급했듯이 동일한 클래스는 초기화 중에 설정되는 매개 변수에 따라 다른 작업을 수행 할 수 있습니다. 클래스가 어떤 용도로 사용되는지 알고 있다면 클래스 재초기화를 생략 할 수 있습니다. 이렇게 하려면 다른 매개 변수를 사용하여 몇 개의 클래스 인스턴스를 로드해야 합니다.

예를 들어, EA가 실행 중일 때 어떤 경우에는 BuyStop 및 BuyLimit 주문을 삭제해야 하는 반면 다른 경우에는 SellStop 및 SellLimit 주문을 삭제해야 합니다. 이 경우 클래스의 두 인스턴스를 로드할 수 있습니다.

BuyStop 및 BuyLimit 주문을 삭제하려면:

COrderDelete DeleteBuy(false,true,false,true,false,false,false);

SellStop 및 SellLimit 주문을 삭제하려면:

COrderDelete DeleteSell(false,false,true,false,true,false,false);

이제 구매 보류 주문을 삭제하려면 클래스의 한 인스턴스를 사용하십시오.

DeleteBuy.Delete();

판매 보류 주문을 삭제하려는 경우 - 다른 인스턴스:

DeleteSell.Delete();

 

객체 배열

프로그램이 실행 중일 때 필요한 클래스 인스턴스 수를 항상 알 수는 없습니다. 이 경우 클래스 인스턴스 (객체)의 배열을 만들 수 있습니다. 생성자와 소멸자가 있는 클래스의 예를 살펴 보겠습니다. 클래스를 약간 변경하여 생성자에 매개 변수를 전달하여 클래스의 각 인스턴스를 모니터링 할 수 있습니다.

// Class
class CName 
  {
private:
   int               m_arg; // Variable for the instance

public:
   // Constructor
   CName(int aArg) 
     {
      m_arg=aArg;
      Alert("Constructor "+IntegerToString(m_arg));
     }
   // Destructor
  ~CName() 
     { 
      Alert("Destructor "+IntegerToString(m_arg)); 
     }
   //---
   void Sleep() 
     { 
      Sleep(3000); 
     }
  };
이 클래스를 사용합시다. 특정 크기의 배열 (예 : 10 개 요소)을 선언할 수 있습니다.
CName* cname[10];

일반적인 변수 선언과의 차이점은 별표 "*" 입니다. 별표는 이전에 사용된 자동 포인터와 달리 동적 포인터가 사용되었음을 나타냅니다.

동적 배열을 사용할 수 있습니다 (사전 할당된 크기없이 동적 배열과 동적 포인터를 혼동하지 마십시오).

CName* cname[];

이 경우 확장이 필요합니다 (모든 함수, 스크립트에서 OnStart() 함수 내부에서 수행됨).

ArrayResize(cname,10);

이제 배열의 모든 요소를 ​​반복하고 각 요소에 클래스 인스턴스를 로드합니다. 이렇게하려면 new 키워드를 사용하십시오.

ArrayResize(cname,10);
for(int i=0; i<10; i++) 
  {
   cname[i]=new CName(i);
  }
중지:
cname[0].Sleep();

스크립트를 확인하십시오. 그것을 실행하고 10 개의 생성자가 있지만 소멸자가 없는지 확인하십시오. 동적 포인터를 사용하면 프로그램이 종료될 때 클래스가 자동으로 언로드되지 않습니다. 또한 "Experts"탭에서 메모리 누수에 대한 메시지를 볼 수 있습니다. 개체를 수동으로 삭제해야 합니다.

for(int i=0; i<10; i++) 
  {
   delete(cname[i]);
  }

이제 스크립트 끝에 10 개의 소멸자가 실행 중이고 오류 메시지가 없습니다.

이 문서에 첨부된 OOP_sConstDestr_2.mq5 파일에서 이 예제를 찾을 수 있습니다.

 

OOP를 사용하여 프로그램 논리 변경 (가상 함수, 다형성)

다형성 - 아마도 이것은 프로그램의 논리를 제어 할 수있는 가장 흥미롭고 중요한 OOP 기능입니다. 가상 함수와 여러 하위 클래스가 있는 기본 클래스를 사용합니다. 한 클래스는 차일드 클래스에서 정의한 여러 형식을 취할 수 있습니다.

간단한 예를 들어 두 값을 비교합니다. 보다 큼 (>),보다 작음 (<),보다 크거나 같음 (>=), 작거나 같음 (<=), 같음 (==)의 다섯 가지 버전의 비교가 있을 수 있습니다.

가상 기능이 있는 기본 클래스를 만듭니다. 가상 함수 - 정확히 동일한 일반 함수이지만 선언은 virtual이라는 단어로 시작합니다.

class CCheckVariant 
  {
public:
   virtual bool CheckVariant(int Var1,int Var2) 
     {
      return(false);
     }
  };

가상 기능에는 코드가 없습니다. 다양한 장치를 수용하는 일종의 커넥터입니다. 장치 유형에 따라 다른 작업을 수행합니다.

5 개의 하위 클래스를 만듭니다.

//+------------------------------------------------------------------+
//|   >                                                              |
//+------------------------------------------------------------------+

class CVariant1: public CCheckVariant
  {
   bool CheckVariant(int Var1,int Var2)
     {
      return(Var1>Var2);
     }
  };
//+------------------------------------------------------------------+
//|   <                                                              |
//+------------------------------------------------------------------+
class CVariant2: public CCheckVariant
  {
   bool CheckVariant(int Var1,int Var2)
     {
      return(Var1<Var2);
     }
  };
//+------------------------------------------------------------------+
//|   >=                                                             |
//+------------------------------------------------------------------+
class CVariant3: public CCheckVariant
  {
   bool CheckVariant(int Var1,int Var2)
     {
      return(Var1>=Var2);
     }
  };
//+------------------------------------------------------------------+
//|   <=                                                             |
//+------------------------------------------------------------------+
class CVariant4: public CCheckVariant
  {
   bool CheckVariant(int Var1,int Var2)
     {
      return(Var1<=Var2);
     }
  };
//+------------------------------------------------------------------+
//|   ==                                                             |
//+------------------------------------------------------------------+
class CVariant5: public CCheckVariant
  {
   bool CheckVariant(int Var1,int Var2)
     {
      return(Var1==Var2);
     }
  };

이 클래스를 사용하기 전에 로드해야 합니다. 어떤 차일드 클래스를 사용해야하는지 알고 있다면 이 차일드의 유형으로 포인터를 선언 할 수 있습니다. 예를 들어 ">"조건을 확인하려면:

CVariant1 var; // Load class to check the ">" condition

저희의 경우와 같이 차일드 유형을 미리 알지 못하면 클래스에 대한 포인터가 기본 클래스 유형으로 선언됩니다. 그러나 이 경우 동적 포인터가 사용됩니다.

CCheckVariant* var;

이 하위 항목은 new 키워드를 사용하여 로드되어야 합니다. 선택한 변형에 따라 하위로드:

// Number of variant
int Variant=5; 
// Depending on variant number one of five children classes will be used
switch(Variant) 
  {
    case 1: 
       var = new CVariant1;
       break;
    case 2: 
       var = new CVariant2;
       break;
    case 3: 
       var = new CVariant3;
       break;
    case 4: 
       var = new CVariant4;
       break; 
    case 5: 
       var = new CVariant5;
       break; 
 }

조건 확인:

bool rv = var.CheckVariant(1,2);

조건을 확인하는 코드가 모든 경우에 동일하더라도 두 값을 비교한 결과는 차일드 클래스에 따라 다릅니다.

이 글 에 첨부된 OOP_sVariant_1.mq5 파일에서 이 예제를 찾을 수 있습니다.

 

캡슐화에 대한 추가 정보 (개인, 보호, 공개)

현재로서는 public 섹션을 통해 매우 명확합니다. in에는 클래스 사용자가 볼 수 있어야 하는 함수와 변수가 포함되어 있습니다. 클래스 사용자 관점에서는 protected 섹션과 private 섹션간에 차이가 없습니다. 이 섹션의 함수와 변수는 사용자가 사용할 수 없습니다.

//+------------------------------------------------------------------+
//|   Class with the protected keyword                               |
//+------------------------------------------------------------------+
class CName1
  {
protected:
   int ProtectedFunc(int aArg)
     {
      return(aArg);
     }
public:
   int PublicFunction(int aArg)
     {
      return(ProtectedFunc(aArg));
     }
  };
//+------------------------------------------------------------------+
//|   Class with the private keyword                                 |
//+------------------------------------------------------------------+
class CName2
  {
private:
   int PrivateFunc(int aArg)
     {
      return(aArg);
     }
public:
   int PublicFunction(int aArg)
     {
      return(PrivateFunc(aArg));
     }
  };

CName1 c1; // Load class with the protected keyword
CName2 c2; // Load class with the private keyword
이 예에는 CName1과 CName2의 두 가지 클래스가 있습니다. 각 클래스에는 두 가지 기능이 있습니다. 하나는 public 섹션에 있고 다른 하나는 protected 섹션 (클래스 CName1의 경우) 또는 private에 있습니다. 섹션 (클래스 CName2 용). 두 클래스 모두 함수 드롭 다운 목록의 public 섹션에 있는 함수 하나만 있습니다 (그림 2 및 3).

그림 2. CName1 클래스의 기능
그림 2. CName1 클래스의 기능

그림 3. CName2 클래스의 기능
그림 3. CName2 클래스의 기능

이 예제는 이 글 에 첨부 된 OOP_sProtPriv_1.mq5 파일에서 찾을 수 있습니다.

privateprotected 섹션은 하위 클래스에 대한 기본 클래스 함수의 가시성을 결정합니다.

//+------------------------------------------------------------------+
//|   Base class                                                     |
//+------------------------------------------------------------------+
class CBase
  {
protected:
   string ProtectedFunc()
     {
      return("CBase ProtectedFunc");
     }
private:
   string PrivateFunc()
     {
      return("CBase PrivateFunc");
     }
public:
   virtual string PublicFunction()
     {
      return("");
     }
  };
//+------------------------------------------------------------------+
//|   Child class                                                    |
//+------------------------------------------------------------------+

class Class: public CBase
  {
public:
   string PublicFunction()
     {
      // With this line everything compiles correctly
      return(ProtectedFunc());
      // If you will uncomment this line and comment the previous one, there will be a compiler error
      // return(PrivateFunc()); 
     }
  };

이 예제에서는 CBase라는 기본 클래스와 Class라는 차일드 클래스가 있습니다. 하위 클래스의 protectedprivate 섹션에 있는 기본 클래스 함수를 호출 해보세요. protected 섹션에서 함수를 호출하면 모든 것이 컴파일되고 실행됩니다. private 섹션에서 함수를 호출하면 컴파일러 오류가 표시됩니다 (개인 멤버 함수를 호출 할 수 없음). 즉, private 섹션의 기능은 하위 클래스에 표시되지 않습니다.

protected 섹션은 클래스 사용자의 함수 만 보호하고 private 섹션은 하위 클래스의 함수도 보호합니다. 차일드 클래스의 기본 클래스 함수 (다른 섹션에 있음)의 가시성은 그림 4에 나와 있습니다.

그림 4. 차일드 클래스의 기본 클래스 함수 가시성
그림 4. 차일드 클래스의 기본 클래스 함수 가시성
파란색 화살표 - 기능 사용 가능, 회색 - 사용 불가능

이 문서에 첨부 된 OOP_sProtPriv_2.mq5 파일에서 이 예제를 찾을 수 있습니다.

 

기본 가상 기능 및 상속

기본 클래스의 모든 가상 함수가 차일드 클래스의 해당 함수를 가져야 하는 것은 아닙니다. 차일드 클래스가 같은 이름의 함수를 가지고 있다면 - 바로 이 함수를 사용할 것이고, 그렇지 않다면 기본 클래스 가상 함수에서 코드를 실행할 것입니다. 예를 들어 고려하십시오.

//+------------------------------------------------------------------+
//|   Base Class                                                     |
//+------------------------------------------------------------------+
class CBase
  {
public:
   virtual string Function()
     {
      string str="";
      str="Function ";
      str=str+"of base ";
      str=str+"class";
      return(str);
     }
  };
//+------------------------------------------------------------------+
//|   Child class 1                                                  |
//+------------------------------------------------------------------+
class Class1: public CBase
  {
public:
   string Function()
     {
      string str="";
      str="Function ";
      str=str+"of child ";
      return(str);
     }
  };
//+------------------------------------------------------------------+
//|   Child class 2                                                  |
//+------------------------------------------------------------------+
class Class2: public CBase
  {

  };

Class1 c1; // Load class 1
Class2 c2; // Load class 2
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   Alert("1: "+c1.Function()); // Running function from Class1
   Alert("2: "+c2.Function()); // Running function from CBase
  }

Class2 클래스에 함수가 없다는 사실에도 불구하고 이 클래스에서 Function() 함수를 호출 할 수 있습니다. 이것은 CBase 클래스에서 함수를 실행합니다. Class1 클래스는 자체 기능을 실행합니다.

void OnStart() 
   {
    Alert("1: " + c1.Function()); // Running function from Class1
    Alert("2: " + c2.Function()); // Running function from CBase
   }

클래스 사용자 관점에서 차일드 클래스를 사용할 때 public 섹션의 기본 클래스의 모든 기능을 사용할 수 있습니다. 이것을 상속이라고 합니다. 기본 클래스의 함수가 virtual로 선언 된 경우 차일드 클래스에 이 이름의 함수가 있으면 차일드 클래스의 함수로 대체됩니다 (그림 5).

그림 5. 클래스 사용자가 함수에 액세스
그림 5. 클래스 사용자가 함수에 액세스

차일드 클래스에 기본 클래스의 가상 함수에 해당하는 함수가없는 경우를 제외하고 차일드 클래스는 "추가" 함수 (기본 클래스 내에 동일한 이름의 가상 함수가 없는 함수)를 가질 수 있습니다. 차일드 클래스 유형에 대한 포인터를 사용하여 클래스를 로드하면 이러한 함수를 사용할 수 있습니다. 기본 클래스 유형에 대한 포인터를 사용하여 클래스를 로드하면 이러한 함수를 사용할 수 없습니다 (그림 6).

그림 6. "추가" 기능의 가시성
그림 6. "추가"기능 (빨간색 화살표)의 가시성이 결정됩니다.
클래스를 로드하는 데 사용되는 포인터 유형별.

이 문서에 첨부 된 OOP_sDefaultVirtual_1.mq5 파일에서 이 예제를 찾을 수 있습니다.

 

클래스 로딩에 대한 추가 정보

가상 함수를 사용할 때 기본 클래스와 차일드 클래스를 사용할 때 어떤 차일드 클래스를 사용해야 하는지 알고 있다면 차일드 클래스에 해당하는 포인터를 사용할 수 있습니다.

Class1 c1; // Load class 1
Class2 c2; // Load class 2

어떤 차일드 클래스를 사용해야하는지 알 수없는 경우 기본 클래스 유형에 대한 동적 포인터를 사용하고 new 키워드를 사용하여 클래스를 로드합니다.

CBase *c; // Dynamic pointer 
void OnStart() 
   {
      c=new Class1; // Load class
      ...

기본 클래스에 대한 자동 포인터를 사용하는 경우

CBase c; // Automatic pointer

기본 클래스는 그대로 사용됩니다. 가상 함수를 호출하면 이러한 함수 내에 있는 코드가 실행됩니다. 가상 기능은 일반 기능으로 변환됩니다.  

 

함수에서 개체 처리

이 섹션의 제목은 자급 자족합니다. 객체에 대한 포인터는 함수에 전달될 수 있으며 함수 내에서 객체 함수를 호출 할 수 있습니다. 함수 매개 변수는 기본 클래스 유형으로 선언 할 수 있습니다. 이것은 기능을 보편적으로 만듭니다. 클래스에 대한 포인터는 참조 (& 표시로 표시)에 의해서만 함수에 전달 될 수 있습니다.

//+------------------------------------------------------------------+
//|   Base Class                                                     |
//+------------------------------------------------------------------+
class CBase
  {
public:
   virtual string Function()
     {
      return("");
     }
  };
//+------------------------------------------------------------------+
//|   Child class 1                                                  |
//+------------------------------------------------------------------+
class Class1: public CBase
  {
public:
   string Function()
     {
      return("Class 1");
     }
  };
//+------------------------------------------------------------------+
//|   Child class 2                                                  |
//+------------------------------------------------------------------+
class Class2: public CBase
  {
public:
   string Function()
     {
      return("Class 2");
     }
  };

Class1 c1; // Load class 1
Class2 c2; // Load class 2
//+------------------------------------------------------------------+
//|   Function to process objects                                    |
//+------------------------------------------------------------------+
void Function(CBase  &c)
  {
   Alert(c.Function());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
// Process objects using one function.
   Function(c1);
   Function(c2);
  }
이 문서에 첨부 된 OOP_sFunc_1.mq5 파일에서 이 예제를 찾을 수 있습니다.

 

함수 및 방법, 변수 및 속성

지금까지 이 글에서는 "기능"이라는 단어를 사용했습니다. 그러나 OOP에서 "기능"이라는 단어 대신 프로그래머는 종종 "방법"이라는 단어를 사용합니다. 클래스를 작성하는 프로그래머의 관점에서 내부에서 클래스를 보면 모든 함수가 함수로 남아 있습니다. 미리 만들어진 클래스를 사용하는 프로그래머의 관점에서 클래스를 보면 public 섹션 (점을 입력 한 후 드롭 다운 목록에서 사용 가능)에 있는 클래스 인터페이스는 메소드라고 불립니다.

메소드 클래스 인터페이스 외에 클래스 속성을 포함할 수 있습니다. public 섹션은 함수뿐만 아니라 변수 (배열 포함)도 포함 할 수 있습니다.

class CMethodsAndProperties 
   {
    public:
        int               Property1; // Property 1
        int               Property2; // Property 2
        void Function1() 
           {
            //...
            return;
           }
        void Function2() 
           {
            //...
            return;
           }
   };

이러한 변수를 클래스 속성이라고 하며 드롭 다운 목록에서도 사용할 수 있습니다 (그림 7).

그림 7. 하나의 목록에있는 클래스의 메소드 및 속성
그림 7. 하나의 목록에있는 클래스의 메소드 및 속성

이러한 속성은 변수와 동일한 방식으로 사용할 수 있습니다.

void OnStart() 
   {
    c.Property1 = 1; // Set property 1
    c.Property2 = 2; // Set property 2

    // Read properties
    Alert("Property1 = " + IntegerToString(c.Property1) + ", Property2 = " + IntegerToString(c.Property2));
   }

이 문서에 첨부된 OOP_sMethodsAndProperties.mq5 파일에서 이 예제를 찾을 수 있습니다.

 

데이터 구조

데이터 구조는 클래스와 비슷하지만 조금 더 쉽습니다. 그렇게 말할 수는 있지만 : 클래스는 데이터 구조와 비슷하지만 오히려 더 복잡합니다. 차이점은 데이터 구조가 변수만 포함할 수 있다는 것입니다. 이와 관련하여 공개, 비공개보호 섹션으로 나눌 필요가 없습니다. 구조의 모든 내용은 이미 public 섹션에 있습니다. 데이터 구조는 struct 단어로 시작하고 그 뒤에 구조 이름이 오고 중괄호 안에 변수를 선언합니다.

struct Str1 
   {
    int    IntVar;
    int    IntArr[];
    double DblVar[];
    double DblArr[];
   };

구조를 사용하려면 변수로 선언해야 하지만 변수 유형 대신 구조 이름을 사용합니다.

Str1 s1;

구조 배열을 선언할 수도 있습니다.

Str1 sar1[];

구조는 변수와 배열뿐만 아니라 다른 구조도 포함 할 수 있습니다.

struct Str2 
   {
    int    IntVar;
    int    IntArr[];
    double DblVar[];
    double DblArr[];
    Str1   Str;
   };

이 경우 구조 2의 일부인 구조 1에서 변수를 호출하려면 두 점을 사용해야 합니다.

s2.Str.IntVar=1;

이 글 에 첨부 된 OOP_Struct.mq5 파일에서 이 예제를 찾을 수 있습니다.

클래스에는 변수 뿐만 아니라 구조도 포함될 수 있습니다.

 

결론

객체 지향 프로그래밍의 요점과 기억해야 할 중요한 순간을 검토해 보겠습니다.

1. 클래스는 class 키워드와 클래스 이름을 사용하여 생성되고 중괄호 안에는 세 섹션으로 작성된 클래스 코드가 있습니다.

class CName 
  {
private:

protected:

public:
  };

2. 클래스의 함수와 변수는 private, protectedpublic의 세 섹션 중 하나에 있을 수 있습니다. private 섹션의 함수와 변수는 클래스 내에서만 사용할 수 있습니다. 클래스 및 하위 클래스에서 사용할 수있는 protected 섹션의 함수 및 변수입니다. public 섹션의 함수와 변수는 모두 사용할 수 있습니다.

3. 클래스 함수는 클래스 내부 또는 외부에 있을 수 있습니다. 클래스 외부에 함수를 배치하는 경우 각 함수 이름 앞에 클래스 이름과 두 개의 콜론을 배치하여 해당 함수가 속한 클래스를 식별해야 합니다.

void ClassName::FunctionName() { ... }

4. 자동 및 동적 포인터를 사용하여 클래스를 로드 할 수 있습니다. 동적 포인터 클래스를 사용할 때는 new 키워드를 사용하여 로드해야 합니다. 이 경우 프로그램을 종료할 때 delete 키워드를 사용하여 개체를 삭제해야 합니다.

5. 하위 클래스가 기본 클래스에 속함을 알리려면 하위 클래스 이름 뒤에 기본 클래스 이름을 추가해야 합니다.

class Class : public CBase { ... }

6. 클래스 초기화 중에는 값으로 변수를 할당 할 수 없습니다. 일부 함수, 더 자주 생성자를 실행하는 동안 값을 할당 할 수 있습니다.

7. 가상 함수는 virtual 키워드를 사용하여 선언됩니다. 차일드 클래스에 같은 이름의 함수가 있으면 바로 이 함수를 실행하고, 그렇지 않으면 기본 클래스의 가상 함수를 실행합니다.

8. 클래스에 대한 포인터를 함수에 전달할 수 있습니다. 기본 클래스 유형으로 함수 매개 변수를 선언 할 수 있으므로 모든 차일드 클래스에 대한 포인터를 함수에 전달할 수 있습니다.

9. public 섹션에는 함수 (메소드) 뿐만 아니라 변수 (속성)도 있습니다.

10. 구조에는 배열 및 기타 구조가 포함될 수 있습니다.

 

첨부 파일 목록

  • OOP_CLibArray_1.mqh - 포함된 파일은 MQL5/Include 폴더에 있어야 합니다. 클래스를 사용하여 라이브러리를 만드는 예. 보호된비공개 키워드. 초과 적재
  • OOP_CLibArray_2.mqh - 포함된 파일, MQL5/Include 폴더에 위치해야 합니다. 클래스를 넘어서 클래스 함수를 배치하는 예.
  • OOP_sDeleteOrders_1.mq5 - 스크립트는 MQL5/Scripts 폴더에 있어야 합니다. 보류중인 주문을 삭제하는 간단한 스크립트입니다.
  • OOP_CDeleteOrder_1.mqh - 포함된 파일은 MQL5/Include 폴더에 있어야 합니다. OOP_sDeleteOrders_1 스크립트를 클래스로 변환하는 예.
  • OOP_sDeleteOrders_2.mq5 - 포함된 파일은 MQL5/Include 폴더에 있어야 합니다. 클래스를 사용하여 주문을 삭제하는 예. OOP_CDeleteOrder_1.mqh 파일에서 가져옵니다 (Init() 함수를 통해 매개 변수 설정).
  • OOP_sConstDestr_1.mq5 - 스크립트는 MQL5/Scripts 폴더에 있어야 합니다. 생성자 및 소멸자 데모.
  • OOP_CDeleteOrder_2.mqh - 포함된 파일, MQL5/Include 폴더에 위치해야 합니다. 생성자로 주문을 삭제하고 생성자를 통해 매개 변수를 전달하는 클래스입니다.
  • OOP_sDeleteOrders_3.mq5 - 스크립트는 MQL5/Scripts 폴더에 있어야 합니다. 클래스를 사용하여 주문을 삭제하는 예. OOP_CDeleteOrder_2.mqh 파일에서 가져옵니다 (생성자를 통해 매개 변수 설정).
  • OOP_sConstDestr_2.mq5 - 스크립트는 MQL5/Scripts 폴더에 있어야 합니다. 클래스를 배열로 로드하는 예.
  • OOP_sVariant_1.mq5 - 스크립트는 MQL5/Scripts 폴더에 있어야 합니다. 차일드이 있는 기본 클래스의 예. 가상 기능, 다형성.
  • OOP_sProtPriv_1.mq5 - 스크립트는 MQL5/Scripts 폴더에 있어야 합니다. 클래스를 사용할 때 protectedprivate 키워드의 ID 예입니다.
  • OOP_sProtPriv_2.mq5 - 스크립트는 MQL5/Scripts 폴더에 있어야 합니다. 하위 클래스의 protectedprivate 키워드에 영향을 미치는 예.
  • OOP_sDefaultVirtual_1.mq5 - 스크립트는 MQL5/Scripts 폴더에 있어야 합니다. 기본 클래스의 가상 기능에 해당하는 기능이없는 차일드 클래스의 예.
  • OOP_sFunc_1.mq5 - 스크립트는 MQL5/Scripts 폴더에 있어야 합니다. 함수에서 객체를 사용하는 예.
  • OOP_sMethodsAndProperties.mq5 - 스크립트는 MQL5/Scripts 폴더에 있어야 합니다. 속성의 예.
  • OOP_Struct.mq5 - 스크립트는 MQL5/Scripts 폴더에 있어야 합니다. 구조의 예.

이러한 파일을 실험 한 후 OOP_CDeleteOrder_2.mqh 및 OOP_sDeleteOrders_3.mq5를 제외한 모든 파일을 삭제할 수 있습니다. OOP_CDeleteOrder_2.mqh 및 OOP_sDeleteOrders_3.mq5 파일은 실제 프로그래밍에 유용할 수 있습니다.

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

파일 첨부됨 |
files_en.zip (11.04 KB)
다중 시간대 및 다중 통화 패널 구축을 위한 객체 지향 접근 방식 다중 시간대 및 다중 통화 패널 구축을 위한 객체 지향 접근 방식
이 글에서는 MetaTrader 5 용 다중 시간 프레임 및 다중 통화 패널을 생성하는데 객체 지향 프로그래밍을 사용하는 방법을 설명합니다. 주요 목표는 패널 자체의 코드를 수정할 필요 없이 가격, 가격 변동, 지표 값 또는 맞춤형 구매/판매 조건과 같은 다양한 종류의 데이터를 표시하는 데 사용할 수 있는 범용 패널을 구축하는 것입니다.
MQL5에서 자신 만의 그래픽 패널 만들기 MQL5에서 자신 만의 그래픽 패널 만들기
MQL5 프로그램의 유용성은 풍부한 기능과 정교한 그래픽 사용자 인터페이스에 의해 결정됩니다. 빠르고 안정적인 작동보다 시각적인식이 때때로 더 중요합니다. 다음은 표준 라이브러리 클래스를 기반으로 디스플레이 패널을 만드는 방법에 대한 단계별 가이드입니다.
세마포어 인디케이터를 사용하는 간단한 거래 시스템 세마포어 인디케이터를 사용하는 간단한 거래 시스템
복잡한 거래 시스템을 철저히 살펴보면 일련의 간단한 거래 신호를 기반으로 한다는 것을 알 수 있습니다. 따라서 초보 개발자가 복잡한 알고리즘 작성을 즉시 시작할 필요가 없습니다. 이 글은 거래를 수행하기 위해 세마포어 인디케이터를 사용하는 거래 시스템의 예를 제공합니다.
MQL5 클라우드 네트워크로 계산 속도 향상 MQL5 클라우드 네트워크로 계산 속도 향상
가정용 컴퓨터에 몇 개의 코어가 있습니까? 거래 전략을 최적화하기 위해 몇 대의 컴퓨터를 사용할 수 있습니까? 여기에서는 MQL5 클라우드 네트워크를 사용하여 마우스 클릭으로 전 세계의 컴퓨팅 성능을 받아 계산을 가속화하는 방법을 보여줍니다. "시간은 돈이다"라는 표현은 해가 갈수록 더욱 화제를 불러 일으키며 수십 시간 또는 며칠 동안 중요한 계산을 기다릴 여유가 없습니다.