کلاس در زبان MQL5 – بخش 2
در مقاله پیشین یعنی ششمین مقاله آموزش زبان mql از سری مقالات مرتبط با برنامهنویسی در بازارهای مالی به شرح و بررسی «کلاس در زبان MQL5» پرداختیم. در هفتمین مقاله این سری نیز به تکمیل و جمعبندی این موضوع میپردازیم.
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
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
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)ها وجود ندارد.
در این مورد، کلاس 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 سایت جهان بورس استفاده نمایید.
دیدگاهتان را بنویسید
برای نوشتن دیدگاه باید وارد بشوید.