کلاس در زبان MQL5 – بخش 2

مبانی زبان MQL5 – بخش 7
مبانی زبان MQL5 – بخش 7

در مقاله‌ی پیشین یعنی ششمین مقاله آموزش زبان mql از سری مقالات مرتبط با برنامه نویسی در بازار‌ها‌ی مالی به شرح و بررسی «کلاس در زبان MQL5» پرداختیم. در هفتمین مقاله‌ی این سری نیز به تکمیل و جمع‌بندی این موضوع می‌پردازیم.

در صورت برقراری شرایط معاملات الگوریتمی در بازار هدف برای اعمال هر یک از این مفاهیم در استراتژی‌های معاملاتی می‌توانید از سفارش کد پایتون، سفارش کد mql و … برای کد کردن و بهینه‌یابی استراتژی‌های معاملاتی خود استفاده کنید. و در عین حال سوای امکان سفارش اکسپرت می‌توانید از سفارش اندیکاتور برای دریافت یک نمایش گرافیکی از محاسبات استراتژی خود بهره ببرید.

– – –

کلاس در زبان MQL5 - بخش 2
کلاس در زبان MQL5 – بخش 2

Constructorها در کلاس در زبان MQL5 

 

یک syntax خاص نیز برای مقداردهی اولیه یک شی با استفاده از constructor وجود دارد. مقداردهی اولیه سازنده (special constructions for initialization) برای اعضای یک ساختار یا کلاس را می توان در initialization list مشخص کرد.

لیست مقداردهی اولیه(initialization list) لیستی از مقداردهی‌های اولیه است که با کاما از هم جدا شده‌اند، که بعد از کولون بعد از لیست پارامترهای سازنده و قبل از بدنه قرار می‌گیرد (قبل از «}»). چندین الزام وجود دارد:

  • Initialization listها را می‌توان فقط در سازنده‌ها استفاده کرد.
  • اعضای والد(Parent members) را نمی‌توان در لیست اولیه مقداردهی اولیه کرد.
  • Initialization listها را باید پس از تعریف (پیاده‌سازی) و پیش از بدنه‌ی یک تابع بیاورید.

در واقع این Syntax به شکل زیر می‌شود.:

  (<pt01> <pn01>=<pv01>, …,<ptn> <pnn>=<pvn>)<ConstructorName>

 ;{<Constructor body>}  (mv01)<mn01> (mv01), …,<mn01> :

— //

pt01  : parameter type01

pn01 : parameter name01

pv01 : parameter value01

… ,

ptn  : parameter typen

pnn : parameter namen

pvn : parameter valuen

… ,

mn01 : member name01

mv01 : member value01

… ,

nn : member namen

mvn : member valuen

… ,

پیش از این به دو راه دیگر نیز برای این منظور اشاره کرده بودیم.، که در زیر تمامی این موارد  برای مفدار دهی اولیه اعضا را مجددا  مرور می‌کنیم. :

Struct members initialization

Struct members initialization
Struct members initialization

1. مقدار دهی اولیه در بدنه ی تابع Constructor

Initialization by a constructor’s body


(<pt01> <pn01>=<pv01>, …,<ptn> <pnn>=<pvn>)<ConstructorName>

  {(mv01) = <mn01> = (mv01), …,<mn01>}

struct test_struct01
    {
     double          a0;
     double          a1;
     uchar           a2;
     //--- Constructor
                     test_struct01() { a0 = 100.0; a1 = 0.0; a2 = 5; }
     //--- Destructor
                    ~test_struct01() { Print("This is the end - test_struct01 "); }
    };

2. مقدار دهی اولیه با بهره‌گیری از syntax خاص (Syntactic sugar)

“Initialization by a constructor with an “initialization list


(<pt01> <pn01>=<pv01>, …,<ptn> <pnn>=<pvn>)<ConstructorName>

;{<Constructor body>}(mv01)<mn01> (mv01), …,<mn01> :

struct test_struct02
    {
     double          a0;
     double          a1;
     uchar           a2;
     //--- Constructor
                     test_struct02(): a0(100.0), a1(0.0), a2(5) {}
     //--- Destructor
                    ~test_struct02() { Print("This is the end - test_struct02 "); }
    };

3. مقدار دهی اولیه با بهره‌گیری از ” initializing sequence “

  • مقدار دهی اولیه در بروسه‌ی instantiation 

“Initialization by an “initializting sequence


 {(mv01) = <mn01> = (mv01), …,<mn01>} = <StructName> <ObjName>

struct test_struct03
    {
     double          a0;
     double          a1;
     uchar           a2;
     //--- Destructor
                    ~test_struct03() { Print("This is the end - test_struct03 "); }
    };
//--- You just can initialize struct members in this way
test_struct03 s03 = {100.0, 0.0, 5};

 

Class members initialization

Class members initialization
Class members initialization

1. مقدار دهی اولیه در بدنه ی تابع Constructor

Initialization by a constructor’s body


(<pt01> <pn01>=<pv01>, …,<ptn> <pnn>=<pvn>)<ConstructorName>

  {(mv01) = <mn01> = (mv01), …,<mn01>}

class TestClass01
    {
private:
     double          b0;
     double          b1;
     uchar           b2;
public:
     //--- Constructor
                     TestClass01() { b0 = 100.0; b1 = 0.0; b2 = 5; Print("TestClass01 b0: ", b0); }
     //--- Destructor
                    ~TestClass01() { Print("This is the end - TestClass01 "); }
    };

 

2. مقدار دهی اولیه با بهره‌گیری از syntax خاص (Syntactic sugar)

“Initialization by a constructor with an “initialization list


(<pt01> <pn01>=<pv01>, …,<ptn> <pnn>=<pvn>)<ConstructorName>

;{<Constructor body>}(mv01)<mn01> (mv01), …,<mn01> :

class TestClass02
    {
private:
     double          b0;
     double          b1;
     uchar           b2;
public:
     //--- Constructor
                     TestClass02(): b0(100.0), b1(0.0), b2(5) { Print("TestClass02 b0: ", b0); }
     //--- Destructor
                    ~TestClass02() { Print("This is the end - TestClass02 "); }
    };
  • نکته: مقدار دهی اولیه با بهره‌گیری از ” initializing sequence ” و یا در واقع مقدار دهی اولیه در پروسه‌ی instantiation در مورد کلاس‌ها قابل انجام نمی‌باشد و صرفا دو مورد مذکور فوق برای مقداردهی اولیه‌ی کلاس‌ها قابل دسترسی خواهند بود.

 

در اینجا مثالی از چندین سازنده برای مقداردهی اولیه اعضای کلاس آورده شده است.

//+------------------------------------------------------------------+
//| A class for storing the name of a character                      |
//+------------------------------------------------------------------+
class CPerson
  {
   string            m_first_name;     // First name 
   string            m_second_name;    // Second name
public:
   //--- An empty default constructor
                     CPerson() {Print(__FUNCTION__);};
   //--- A parametric constructor
                     CPerson(string full_name);
   //--- A constructor with an initialization list
                     CPerson(string surname,string name): m_second_name(surname), m_first_name(name) {};
   void PrintName(){PrintFormat("Name=%s Surname=%s",m_first_name,m_second_name);};
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPerson::CPerson(string full_name)
  {
   int pos=StringFind(full_name," ");
   if(pos>=0)
     {
      m_first_name=StringSubstr(full_name,0,pos);
      m_second_name=StringSubstr(full_name,pos+1);
     }
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Get an error "default constructor is not defined"
   CPerson people[5];
   CPerson Tom="Tom Sawyer";                       // Tom Sawyer
   CPerson Huck("Huckleberry","Finn");             // Huckleberry Finn
   CPerson *Pooh = new CPerson("Winnie","Pooh");  // Winnie the Pooh
   //--- Output values
   Tom.PrintName();
   Huck.PrintName();
   Pooh.PrintName();
   
   //--- Delete a dynamically created object
   delete Pooh;
  }
  • در instantiation از کلاس‌(class)ها فارغ از این که از کدام Constructor موجود در کلاس‌ها استفاده شود.، می‌توان از new» Operator» استفاده کرد. ولی امکان استفاده از این Operator در ساختار(struct)ها وجود ندارد.
instantiation
instantiation

در این مورد، کلاس CPerson دارای سه سازنده است:

  • یک سازنده پیش فرض صریح، که اجازه ایجاد آرایه‌ای از اشیاء این کلاس را می‌دهد.
  • سازنده‌ای با یک پارامتر که یک نام کامل به عنوان پارامتر دریافت می‌کند و با توجه به فضای پیدا شده آن را به نام و نام دوم تقسیم می‌کند.
  • سازنده‌ای با دو پارامتر که حاوی لیست اولیه است. m_second_name(surname) and m_first_name(name)

توجه داشته باشید که مقداردهی اولیه با استفاده از یک لیست جایگزین یک انتساب شده است. اعضای منفرد باید به صورت زیر مقداردهی شوند:

class_member (a list of expressions)
//

در لیست اولیه، اعضا می توانند به هر ترتیبی بروند، اما همه اعضای کلاس بر اساس ترتیب اعلام شده خود مقداردهی اولیه می‌شوند. این بدان معنی است که در سازنده سوم، ابتدا عضو m_first_name، همانطور که ابتدا اعلام می‌شود مقداردهی اولیه می‌شود و تنها پس از آن که m_second_name مقداردهی اولیه شد. این باید در مواردی در نظر گرفته شود که مقداردهی اولیه برخی از اعضای کلاس به مقادیر موجود در سایر اعضای کلاس بستگی دارد.

  • اگر یک سازنده پیش‌فرض در کلاس پایه اعلان نشده باشد و هم‌زمان یک یا چند سازنده با پارامترها اعلان شود، همیشه باید یکی از سازنده‌های کلاس پایه را در لیست اولیه فراخوانی کنید. به عنوان اعضای معمولی لیست از کاما عبور می‌کند و در طول مقداردهی اولیه شی، بدون توجه به اینکه در کجای فهرست اولیه قرار دارد، ابتدا فراخوانی می‌شود.
//+------------------------------------------------------------------+
//| Base class                                                       |
//+------------------------------------------------------------------+
class CFoo
  {
   string            m_name;
public:
   //--- A constructor with an initialization list
                     CFoo(string name) : m_name(name) { Print(m_name);}
  };
//+------------------------------------------------------------------+
//| Class derived from CFoo                                          |
//+------------------------------------------------------------------+
class CBar : CFoo
  {
   CFoo              m_member;      // A class member is an object of the parent
public:
   //--- A default constructor in the initialization list calls the constructor of a parent
                     CBar(): m_member(_Symbol), CFoo("CBAR") {Print(__FUNCTION__);}
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   CBar bar;
  }

در این مثال، هنگام ایجاد آبجکت bar، یک سازنده پیش‌فرض ()CBar فراخوانی می‌شود که در آن ابتدا یک سازنده برای CFoo والد فراخوانی می‌شود و سپس یک سازنده برای عضو کلاس m_member می‌آید.

Destructor یک تابع ویژه است که به طور خودکار هنگام از بین رفتن یک شی کلاس فراخوانی می‌شود. نام destructor به عنوان نام کلاس با یک tilde (~) نوشته می‌شود. رشته‌ها، آرایه‌های دینامیک و اشیایی که نیاز به مقداردهی اولیه دارند، بدون توجه به وجود یا عدم حضور destructor، به هر حال از مقدار اولیه حذف می‌شوند. در صورت وجود destructor، این اقدامات پس از فراخوانیdestructor انجام می‌شود.

Destructor ها همیشه virtual هستند، صرف نظر از اینکه با کلمه کلیدی virtual تعریف شده باشند یا خیر.

تعریف Class Methods

  • متدهای تابع کلاس هم در داخل کلاس و هم در خارج از تعریف کلاس قابل تعریف هستند.
    • اگر متد در یک کلاس تعریف شده باشد، بدنه آن درست بعد از تعریف متد می‌آید.
class CTetrisShape
  {
protected:
   int               m_type;
   int               m_xpos;
   int               m_ypos;
   int               m_xsize;
   int               m_ysize;
   int               m_prev_turn;
   int               m_turn;
   int               m_right_border;
public:
   void              CTetrisShape();
   void              SetRightBorder(int border) { m_right_border=border; }
   void              SetYPos(int ypos)          { m_ypos=ypos;           }
   void              SetXPos(int xpos)          { m_xpos=xpos;           }
   int               GetYPos()                  { return(m_ypos);        }
   int               GetXPos()                  { return(m_xpos);        }
   int               GetYSize()                 { return(m_ysize);       }
   int               GetXSize()                 { return(m_xsize);       }
   int               GetType()                  { return(m_type);        }
   void              Left()                     { m_xpos-=SHAPE_SIZE;    }
   void              Right()                    { m_xpos+=SHAPE_SIZE;    }
   void              Rotate()                   { m_prev_turn=m_turn; if(++m_turn>3) m_turn=0; }
   virtual void      Draw()                     { return;                }
   virtual bool      CheckDown(int& pad_array[]);
   virtual bool      CheckLeft(int& side_row[]);
   virtual bool      CheckRight(int& side_row[]);
  };

توابع از SetRightBorder (int border) تا ()Draw مستقیماً در کلاس CTetrisShape تعریف می‌شوند.

سازنده ()CTetrisShape و متدهای CheckDown(int& pad_array[]) ، CheckLeft(int&side_row[]) و CheckRight(int&side_row[]) فقط در داخل کلاس تعریف شده‌اند، اما هنوز تعریف نشده‌اند. تعاریف این توابع بیشتر در کد خواهد بود. برای تعریف متد خارج از کلاس از عملگر scope resolution استفاده می‌شود و از نام کلاس به عنوان scope استفاده می‌شود.

//+------------------------------------------------------------------+
//| Constructor of the basic class                                   |
//+------------------------------------------------------------------+
void CTetrisShape::CTetrisShape()
  {
   m_type=0;
   m_ypos=0;
   m_xpos=0;
   m_xsize=SHAPE_SIZE;
   m_ysize=SHAPE_SIZE;
   m_prev_turn=0;
   m_turn=0;
   m_right_border=0;
  }
//+------------------------------------------------------------------+
//| Checking ability to move down (for the stick and cube)           |
//+------------------------------------------------------------------+
bool CTetrisShape::CheckDown(int& pad_array[])
  {
   int i,xsize=m_xsize/SHAPE_SIZE;
//---
   for(i=0; i<xsize; i++)
     {
      if(m_ypos+m_ysize>=pad_array[i]) return(false);
     }
//---
   return(true);
  }

Public” , “Protected” and “Private” Access Specifiers”

هنگام توسعه یک کلاس جدید، توصیه می‌شود دسترسی اعضا را از خارج محدود کنید. برای این منظور از کلمات کلیدی Private یا Protected استفاده می‌شود.

در این مورد، hidden data را می‌توان تنها از طریق Class Method ‌های همان کلاس در دسترس قرار داد. اما اگر از کلمه کلیدی Protected استفاده شود، می‌توان به hidden data از Class Method وارثان این کلاس نیز دسترسی داشت. از همین روش می توان برای محدود کردن دسترسی به Class Method ها نیز استفاده کرد.

اگر نیاز دارید که دسترسی کامل به اعضا و/یا متدهای یک کلاس داشته باشید.، از کلمه کلیدی public استفاده کنید.

class CTetrisField
  {
private:
   int               m_score;                            // Score
   int               m_ypos;                             // Current position of the figures
   int               m_field[FIELD_HEIGHT][FIELD_WIDTH]; // Matrix of the well
   int               m_rows[FIELD_HEIGHT];               // Numbering of the well rows
   int               m_last_row;                         // Last free row
   CTetrisShape     *m_shape;                            // Tetris figure
   bool              m_bover;                            // Game over
public:
   void              CTetrisField() { m_shape=NULL; m_bover=false; }
   void              Init();
   void              Deinit();
   void              Down();
   void              Left();
   void              Right();
   void              Rotate();
   void              Drop();
private:
   void              NewShape();
   void              CheckAndDeleteRows();
   void              LabelOver();
  };

هر عضو کلاس و متدهای اعلام شده بعد از مشخص کننده public : (و قبل از مشخص کننده دسترسی بعدی) در هر ارجاع به شی کلاس توسط برنامه در دسترس هستند. در این مثال اینها اعضای زیر هستند: توابع ()CTetrisField() ،Init() ،Deinit() ،Down() ،Left() ،Right() ،Rotate و ()Drop.

هر عضوی که بعد از مشخص‌کننده دسترسی به عناصر private : (و قبل از مشخص‌کننده دسترسی بعدی) اعلام می‌شود، فقط برای اعضای-توابع این کلاس در دسترس است. مشخص‌کننده‌های دسترسی به عناصر همیشه با کولون (:) ختم می‌شوند و می‌توانند بارها در تعریف کلاس ظاهر شوند.

هر عضو کلاسی که پس از مشخص‌کننده دسترسی protected : تعریف شده است (و تا تعیین‌کننده دسترسی بعدی) فقط برای اعضای-توابع این کلاس و اعضای-توابع از فرزندان کلاس در دسترس است. هنگام تلاش برای ارجاع به اعضای دارای مشخصه های private و protected از خارج، خطای مرحله کامپایل را دریافت می‌کنیم. مثال:

class A
  {
protected:
   //--- the copy operator is available only inside class A and its descendants
   void operator=(const A &)
     {
     }
  };
class B
  {
   //--- class A object declared
   A                 a;
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   //--- declare two B type variables
   B b1, b2;
   //--- attempt to copy one object into another
   b2=b1;
  }

هنگام کامپایل کردن کد، پیام خطا دریافت می‌شود – —

an attempt to call the remote copy operator:

attempting to reference deleted function 'void B::operator=(const B&)'   trash3.mq5   32   6
//

رشته دوم زیر توضیحات مفصل‌تری ارائه می‌دهد – عملگر کپی در کلاس B به صراحت حذف شد، زیرا عملگر غیرقابل دسترس کپی از کلاس A فراخوانی شده است.

function 'void B::operator=(const B&)' was implicitly deleted because it invokes inaccessible function 'void A::operator=(const A&)'
//

دسترسی به اعضای کلاس پایه را می‌توان در طول وراثت در کلاس های مشتق شده دوباره تعریف کرد.

 

delete’ specifier’

 

مشخص‌کننده delete، توابع و اعضای کلاس را که قابل استفاده نیستند، علامت گذاری می‌کند. این بدان معنی است که اگر برنامه به طور صریح یا ضمنی به چنین عملکردی اشاره کند، خطا در مرحله کامپایل دریافت شده است.

به عنوان مثال، این مشخص کننده(specifier) به شما اجازه می‌دهد تا متدهای والد را در کلاس فرزند غیرقابل دسترس کنید. اگر تابع را در ناحیه private کلاس والد تعریف کنیم، می‌توان به همین نتیجه دست یافت. در اینجا، استفاده از delete باعث می‌شود کد در سطح فرزندان قابل خواندن و مدیریت باشد.

 

در مقالات بعدی آموزش ام کیو ال، به مجموعه‌ها (union) و Interface ها در زبان MQL5 خواهیم داد.

همچنین می‌توانید از سسله مقالات آموزش Python سایت جهان بورس استفاده نمایید.

0 0 رای ها
امتیازدهی به مقاله
0 نظرات
بازخورد (Feedback) های اینلاین
مشاهده همه دیدگاه ها
سبد خرید
هیچ محصولی در سبد خرید وجود ندارد!
خرید را ادامه دهید