기고글 토론 "MQL5에서 객체 포인터 사용" - 페이지 3

 
이러한 경우에는 포인터를 사용하지 마세요.
 
Barbarian2:

MQL5와 현재 MQL4의 포인터와 참조에 대해 여전히 이해하지 못합니다. 추가 코드를 제외하고 참조와 포인터로 전달하는 것의 차이점은 무엇인가요? C++에는 차이가 있지만 여기에는 무엇이 있습니까? 더 자세히 작성하는 것이 어렵지 않다면.

참조로 전달하려면 참조로 전달된 객체를 초기화해야 합니다. 포인터로 전달할 때는 이러한 제한이 없습니다:

class CShape
{
   public:
      string name;
};

void OnInit()
{
   CShape* shape = NULL; 
   TestShapePointer(shape);
   //Critical Error!
   TestShapeRef(shape);
   
}

void TestShapeRef(CShape& shape)
{
   printf(shape.name);
}

void TestShapePointer(CShape* shape)
{
   if(CheckPointer(shape) != POINTER_INVALID)
      printf(shape.name);
}

테스트 셰이프 참조 함수를 호출할 때 셰이프가 초기화되지 않아 코드가 충돌합니다. 반면 TestShapePointer 함수 내부에서는 전달된 객체가 초기화되었는지 여부를 지속적으로 확인해야 합니다. 따라서 비경험적 규칙을 따르세요:

함수에 전달되는 시점에 객체의 존재가 보장되어야 하는 경우 "&" 전달을 사용합니다. 함수에 상태가 정의되지 않은 객체가 필요한 경우 "*" 포인터로 전달하고 이 함수 내부에서 전달된 포인터의 유효성을 확인합니다.

명심해야 할 미묘한 뉘앙스가 하나 더 있습니다. 이전 예제를 생각해 보세요:

class CShape
{
   public:
      string name;
};

void OnInit()
{
   CShape* shape = NULL; 
   TestShapePointer(shape);
   printf(shape.name); //ERROR (!?)
}

void TestShapePointer(CShape* shape)
{
   if(CheckPointer(shape) == POINTER_INVALID)
      shape = new CShape();
   printf(shape.name);
}
이 프로그램이 제대로 작동할까요? 테스트 셰이프 포인터 함수에서 객체를 생성하는 것이 보장된 것처럼 보이지만, "잘못된 포인터 액세스"라는 오류로 끝납니다. printf(shape.name); //ERROR (!?) 줄에서. 요점은 실제로 모양 대신 NULL 참조가 전달되었다는 것입니다. 즉, 함수 내부의 모양과 전달된 모양은 서로 다른 객체입니다! 따라서 함수를 종료한 후에도 모양은 여전히 NULL과 같고 함수 내부의 모양 포인터는 손실됩니다(스택에서 지워짐). 그래서
 

서로 다른 유형의 객체로 배열을 만들 수 있나요?

이 예제를 살펴봅시다:

//+------------------------------------------------------------------+
//|#Test.mq5 |
//| 저작권 2014, 메타쿼츠 소프트웨어 주식회사 | | |
//|http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
//---
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//---
CChartObjectRectLabel rect_label;
CChartObjectButton    button;
CChartObjectEdit      edit;
//---
CChartObject *objects[];
//+------------------------------------------------------------------+
//| 스크립트 프로그램 시작 기능|
//+------------------------------------------------------------------+
void OnStart()
  {
   edit.Create(0,"edit_1",0,20,20,100,20);
   button.Create(0,"button_1",0,20,40,100,20);
   rect_label.Create(0,"rect_label_1",0,20,60,100,20);
//---
   AddObjectToArray(edit);
   AddObjectToArray(button);
   AddObjectToArray(rect_label);
//---
   //objects[0]. // CChartObjectEdit 클래스의 메서드에 액세스하는 방법은 무엇인가요?
   //objects[1]. // CChartObjectButton 클래스의 메서드에 액세스하는 방법은 무엇인가요?
   //objects[2]. // CChartObjectRectLabel 클래스의 메서드에 액세스하는 방법은 무엇인가요?
//---
   while(!IsStopped()) Sleep(1000);
  }
//+------------------------------------------------------------------+
//| 객체 수를 반환합니다.|
//+------------------------------------------------------------------+
int ElementsTotal()
  {
   return(ArraySize(objects));
  }
//+------------------------------------------------------------------+
//|| 배열에 객체를 추가합니다.|
//+------------------------------------------------------------------+
void AddObjectToArray(CChartObject &object)
  {
   int size=ElementsTotal();
//---
   ArrayResize(objects,size+1);
   objects[size]=GetPointer(object);
//---
   if(CheckPointer(objects[size])!=POINTER_INVALID)
      Print(__FUNCSIG__," >>> array["+IntegerToString(size)+"]: ",objects[size].Name(),
            "; object type: ",ObjectTypeToString(objects[size].Type()));
  }
//+------------------------------------------------------------------+
//| 개체 유형을 문자열로 변환합니다.|
//+------------------------------------------------------------------+
string ObjectTypeToString(int type)
  {
   string str="";
//---
   switch((ENUM_OBJECT)type)
     {
      case OBJ_EDIT            : return("edit");            break;
      case OBJ_BUTTON          : return("button");          break;
      case OBJ_RECTANGLE_LABEL : return("rectangle label"); break;
     }
//---
   return("undefined object type");
  }
//+------------------------------------------------------------------+

//---

상속된 클래스의 메서드에 액세스하려면 어떻게 해야 하나요?

 
tol64:

상속자 클래스의 메서드에 어떻게 접근하나요?

대상 유형으로 형변환을 시도해 보셨나요?
 
Rosh:
타겟 유형을 캐스팅해 보셨나요?
아니요, 처음 들어봤습니다. 어디서 확인할 수 있나요?
 
tol64:
아니요, 처음 들어봤습니다. 어디에서 읽을 수 있나요?
일반적인 유형의 형용사이며, 여기에 예가 있습니다:

//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
class CFoo
  {
  };
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
class CBar : public CFoo
  {
public:
   void func(int x) { Print("Hello from Bar ",x); }
  };
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
void func(CFoo *foo)
  {
포인터에서 기본 유형으로 직접 캐스팅하여 호출 //--- 호출
   ((CBar*)foo).func(1);
대상 유형으로 변환하고 필요한 유형으로 포인터 사본을 저장하여 //--- 호출합니다.
   CBar *b=(CBar *)foo;
   b.func(2);
//--- 동적으로 생성된 클래스 삭제
   delete foo;
  }
//+------------------------------------------------------------------+
//||
//+------------------------------------------------------------------+
void OnStart()
  {
   func(new CBar);
  }
//+------------------------------------------------------------------+
 
mql5:
일반적인 유형 변환의 예는 다음과 같습니다:

감사합니다. 살펴볼게요.
 
Rosh:
대상 유형으로 캐스팅을 시도해 보셨나요?
젠장, 그다음에 언어 안전에 대해 얘기하는 건가요?
 
TheXpert:
젠장, 그다음에 언어 보안을 얘기하는 건가요?
보안 허점을 찾았나요? )
 

다형성을 사용하는 것이 더 낫지 않을까요?

이렇게 하면 됩니다:

CChartObject *ptr_rect_label;
CChartObject *ptr_button;
CChartObject *ptr_edit;
CChartObject *objects[];
//---
ptr_rect_label=new CChartObjectRectLabel;
ptr_button=new CChartObjectButton;
ptr_edit=new CChartObjectEdit;