템플릿 이점

함수 템플릿 은 배열에서 최대 요소 검색과 같이 다양한 데이터 유형에 대해 유사한 작업을 수행해야 할 때 사용됩니다.  템플릿을 적용하는 가장 큰 장점은 각 유형에 대해 별도의 오버로드코드를 지정할 필요가 없다는 것입니다.  각 유형의 오버로드를 여러 개 선언하는 대신

double ArrayMax(double array[])
  {
   ...
  }
int ArrayMax(int array[])
  {
   ...
  }
uint ArrayMax(uint array[])
  {
   ...
  }
long ArrayMax(long array[])
  {
   ...
  }
datetime ArrayMax(datetime array[])
  {
   ...
  }

템플릿 함수는 하나만 작성하면 됩니다

template<typename T> 
T ArrayMax(T array[])
  {
   if(ArraySize()==0) 
      return(0);
   uint max_index=ArrayMaximum(array);  
   return(array[max_index]);
  }

코드에 사용하기 위해:

double high[];
datetime time[];
....
double max_high=ArrayMax(high);
datetime lasttime=ArrayMax(time);

여기서 사용된 데이터 유형을 지정하는 T 형식 매개변수는 컴파일하는 동안 실제로 적용된 형식으로 대체됩니다. 즉, 컴파일러는 각 유형 (double, datetime, 등)에 대해 별도의 함수를 자동으로 생성합니다. 또한 MQL5를 사용하면 접근 방식의 모든 장점을 사용하여 클래스 템플릿을 개발할 수 있습니다.

클래스 템플릿

클래스 템플릿은 template 키워드를 사용하여 선언되며, 형식 매개 변수 목록을 열거하는 꺽쇠 괄호 <> 를 사용하여 typename 키워드와 함께 선언됩니다. 이 항목은 컴파일러에게 클래스를 구현할 때 실제 변수 유형을 정의하는 T 형식 매개 변수를 가진 일반 클래스를 처리함을 알려줍니다. 예를 들어, T 유형 요소가 있는 배열을 저장하기 위한 벡터 클래스를 생성하겠습니다:

#define TOSTR(x) #x+" "   // 객체명 표시 매크로
//+------------------------------------------------------------------+
//| T형 요소 저장을 위한 벡터 클래스                                     |
//+------------------------------------------------------------------+
template <typename T>
class TArray
  {
protected:
   T                 m_array[];
public:
   //--- 생성자는 기본적으로 10개 요소의 배열을 작성합니다
   void TArray(void){ArrayResize(m_array,10);}
   //--- 지정된 배열 크기를 사용하여 벡터 생성자
   void TArray(int size){ArrayResize(m_array,size);}
   //--- TARray 유형 개체에 저장된 데이터 유형과 양을 반환합니다
   string Type(void){return(typename(m_array[0])+":"+(string)ArraySize(m_array));};
  };

다음으로, 다양한 유형으로 작업할 수 있도록 프로그램에서 세 개의 TArray 개체를 만들기 위해 다른 방법을 적용하겠습니다

void OnStart()
  {
   TArray<double> double_array;   // 벡터의 기본 크기는 10입니다 
   TArray<int> int_array(15);     // 벡터의 크기는 15입니다
   TArray<string> *string_array;  // TArray<string> 벡터에 대한 포인터 
//--- 동적 객체 생성
   string_array=new TArray<string>(20);
//--- 저널에 객체 이름, 데이터 유형 및 벡터 크기 표시
   PrintFormat("%s (%s)",TOSTR(double_array),double_array.Type());
   PrintFormat("%s (%s)",TOSTR(int_array),int_array.Type());
   PrintFormat("%s (%s)",TOSTR(string_array),string_array.Type());
//--- 프로그램을 완료하기 전에 동적 객체 제거
   delete(string_array);   
  }

스크립트 실행 결과:

  double_array  (double:10)
  int_array  (int:15)
  string_array  (string:20)

이제 데이터 유형이 다른 벡터 3개가 있습니다: double, int, string.

클래스 템플릿은 모든 유형의 다른 개체를 캡슐화하기 위해 설계된 개체인 컨테이너 개발에 적합합니다. 컨테이너 개체는 이미 하나의 특정 유형의 개체를 포함하는 컬렉션입니다. 일반적으로 저장된 데이터로 작업하는 것은 컨테이너에 즉시 내장됩니다.

예를 들어, 배열 외부에 있는 요소에 대한 액세스를 허용하지 않는 클래스 템플릿을 작성하여 "범위를 벗어남" 심각 오류를 방지할 수 있습니다.

//+------------------------------------------------------------------+
//| 배열 요소에 대한 자유 액세스를 위한 클래스                            |
//+------------------------------------------------------------------+
template<typename T>
class TSafeArray
  {
protected:
   T                 m_array[];
public:
   //--- 기본 구성자
   void              TSafeArray(void){}
   //--- 지정된 크기의 배열을 만들기 위한 생성자
   void              TSafeArray(int size){ArrayResize(m_array,size);}
   //--- 배열 크기 
   int               Size(void){return(ArraySize(m_array));}
   //--- 배열 크기 변경 
   int               Resize(int size,int reserve){return(ArrayResize(m_array,size,reserve));}
   //--- 배열 릴리즈 
   void              Erase(void){ZeroMemory(m_array);}
   //--- 인덱스로 배열 요소에 액세스하기 위한 연산자
   T                 operator[](int index);
   //--- 배열에서 모든 요소를 한 번에 수신하기 위한 대입 연산자
   void              operator=(const T  &array[]); // T 유형 배열 
  };
//+------------------------------------------------------------------+
//| 인덱스별 요소 수신                                                 |
//+------------------------------------------------------------------+
template<typename T>
T TSafeArray::operator[](int index)
  {
   static T invalid_value;
//---
   int max=ArraySize(m_array)-1;
   if(index<0 || index>=ArraySize(m_array))
     {
      PrintFormat("%s 인덱스 %d 은(는) 다음 범위에 없습니다: (0-%d)!",__FUNCTION__,index,max);
      return(invalid_value);
    }
//---
   return(m_array[index]);
  }
//+------------------------------------------------------------------+
//| 배열에 할당                                                       |
//+------------------------------------------------------------------+
template<typename T>
void TSafeArray::operator=(const T  &array[])
  {
   int size=ArraySize(array);
   ArrayResize(m_array,size);
//--- T 유형이 복사 연산자를 지원해야 합니다
   for(int i=0;i<size;i++)
      m_array[i]=array[i];
//---
  }
//+------------------------------------------------------------------+
//| 스크립트 프로그램 시작 함수                                         |
//+------------------------------------------------------------------+
void OnStart()
  {
   int copied,size=15;  
   MqlRates rates[];
//--- 값 배열 복제
   if((copied=CopyRates(_Symbol,_Period,0,size,rates))!=size)
     {
      PrintFormat("CopyRates(%s,%s,0,%d) 가 %d 에러코드를 반환했습니다",
      _Symbol,EnumToString(_Period),size,GetLastError());
      return;
    }
//--- 컨테이너를 만들고 MqlRates 값 배열을 컨테이너에 삽입합니다
   TSafeArray<MqlRates> safe_rates;
   safe_rates=rates;
   //--- 배열 내에서 색인화
   int index=3;
   PrintFormat("Close[%d]=%G",index,safe_rates[index].close);
   //--- 배열 외부 인덱스
   index=size;
   PrintFormat("Close[%d]=%G",index,safe_rates[index].close);
  }

클래스 선언 외부의 메서드를 설명할 때도 템플릿 선언을 사용해야 합니다:

template<typename T>
T TSafeArray::operator[](int index)
  {
   ...
  }
template<typename T>
void TSafeArray::operator=(const T  &array[])
  {
   ...
  }

클래스 및 함수 템플릿을 사용하여 "키 – 값" 쌍을 저장하기 위한 맵 컬렉션과 같이 쉼표로 구분된 여러 형식 매개변수를 정의할 수 있습니다:

template<typename Key, template Value>
class TMap
  {
   ...
  }

 

더 보기

함수 템플릿, 오버로드