تبليغاتX
C++ آموزش - جلسه ی شانزدهم (شی گرایی قسمت اول _ آشنایی با نحوه ی ساخت يك شی ساده و تابع سازنده)

(این تعریف به مرور در جلسات آینده کامل تر می شود.)

}نام کلاسclass

public:

اعلان توابع عضو

اعلان فیلد ها (متغیر های عضو)

};

نکته:

1.روند تعریف توابع عضو و فیلد ها از هیچ تر تیبی پیروی نمی کند.

2.کلمه ی کلیدی public به این معنی است که تمام اعضا تعریف شده بعد از این کلمه قابل دسترس همه اشیا برنامه است در این رابطه در جلسات بعد صحبت می کنیم فعلا فقط بدانید که باید آن را بنویسید!

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

تعریف تابع::نام کلاس نوع باز گشتی

{

دستورات تابع

}

خب تئوری کافیه دیگه حالا مبی خوایم یه کلاس تعریف کنیم و از اشیائ ساخته شده از آن استفاده کنیم!

class MyClass{

public:

int a,b;

float GetAsFloat();

void InverseIt();

void Add(MyClass tmp);

};

 

float MyClass::GetAsFloat()

{

 return (float)a/b;

}

 

void MyClass::InverseIt()

{

int tmp=a; a=b;b=tmp;

}

 

void MyClass::Add(MyClass tmp)

{

a = a * tmp.b + tmp.a * b;

b *= tmp.b;

}

در این کلاس سعی بر پیاده سازی اعداد کسری بوده. بدبن منظور فیلد های a,bبرای مخرج و صورت کسر در نظر گرفته شده. چند تابع مفید ورایج هم برای انجام برخی کارهای رایج مثل گرفتن عدد اعشاری متناظر با کسر ,معکوس کردن و همچنین جمع کردن یک کسر با کسر دیگر در نظر گرفته شده.به نحوه تعریف بدنه ی توابع عضو خارج از بدنه ی کلاس توجه کنید.همانند تعریف همه ی توابع است فقط عبارت MyClass:: به ابتدای نام کلاس اضافه شده.

حالا یک برنامه می نویسیم که از این کلاس استفاده کند و عملکرد اعضا کلاس را بررسی می کنیم

int main(){

MyClass n1,n2;

n1.a=2;

n1.b=3;

n2.a=1;

n2.b=4;

cout<

n1.InverseIt();

cout<

n1.Add(n2);

cout<

n2.Add(n1);

cout<

getch();

return 0;

}

در این برنامه بعد از نوشتن تعریف کلاس اعداد کسری در ابتدای برنامه دو عدد کسری n1,n2را ایجاد نمودیم و اعداد 2/3,1/4را در این اعداد ذخیره کردیم(خطوط دوم تا ششم)

خب حالا می خوایم عدد اعشاری معادل با n1رو که به صورت کسر2/3در آن ذخیره شده چاپ کنیم. به نظرتون آیا خودمون صورت کسر رو بر مخرج آن تقسیم کنیم خوبه؟؟.........

نه ... فکرش رو هم نکن n1خودش این کارو خوب بلده ! چون یه تابع داره که این کار رو انجام میده کافیه فقط ازش بخواین اونم اما چه جوری خب خیلی سادس برای دسترسی به اعضای کلاس باید از عملگر نقطه استفاده کنیم یعنی مثلا برای فرا خوانی تابع مورد نظرمون که در اینجا تابعGetAsFloat()هست کافیه بنوسیم n1.GetAsFloat();می بینید هیچ فرقی باتابع معمولی که عضو هیچ کلاسی نیست نداره مثل اینه که اسمش به جای GetAsFloat(),  n1.GetAsFloat() باشه در ضمن دسترسی به فیلد های کلاس(متغیر های عضو) هم همینطوره که در اینجا می تونیم فرض کنیم نام مثلا فیلد

aبه صورت n1.a خب پس تابع رو فراخوانی کردیم و مقدار بازگشتی تابع رو با دستور cout چاپ کردیم

 

نکته:

هیچ می دانستید cinوcoutخودشان اشیائی از کلاس iostreamهستند! پس از این به بعد بجای عبارات اشتباه تابع cin(cout) یا دستور cin(cout) می گوییم شئ cin(cout)

 

خب حالا به ادامه ی اجرا برنامه می پردازیم تااینجا مقدار اعشاری عددn1را چاپ کردیم بدون اینکه بدانیم این کار توسط این شئ چگونه انجام می شود.در خط هشتم تابع InverseIt() را فراخوانی نموده ایم.این تابع عدد کسری ذخیره شده در شئ را معکوس می کند! باز هم بدون این که بدانیم این کار چگونه انجام می شود! نکته ای که اینجا به ذهن می رسد و شاید تا به حال هم کمی ابهام ایجاد کرده باشد این است که این تابع و البته توابع دیگر عضو کلاس چگونه بدون داشتن هیچ آرگمانی به فیلد های aوbدسترسی دارند!؟ و این تابع چگونه محتوبات فیلد های مذکور را جابجا می کند! در حالی که هیچ آرگمانی برای دریافت این فیلد ها ندارد!! در اصطلاح می گویند اعضا کلاس(در اینجا منظور فقط توابع عضو هستند) یک اشاره گر مخفی دارند یا بهشون فرستاده می شه که اسمش اشاره گر thisهست این اشاره گر چیزی جز یک اشاره گر به شیی که تابع رو فرا خوانی کرده نیست و دقیقا از نوع اشاره گر خود کلاسی هست که تابع عضوی از اونه در نتیجه به واسطه ی حضور این اشاره گر درون توابع گویی خود شی فراخوانی کننده ی تابع, درون تابع حضور داره و قابل دسترسی هست! پس در واقع تابع InverseIt() رو می تونستیم این طور بنویسیم

void MyClass::InverseIt()

{

int tmp = this->a;

this->a = this->b;

this->b = tmp;

}

اما خوشبختانه کامپایلر از مون نمی خواد که حتما این عبارت نه چندان زیبای thisرو بنویسیم و خودش به صورت ضمنی می دونه که منظورمون this->……هست

 

نکته:

1.برای دسترسی به اعضا یک شی از طریق اشاره گر آن به جای عملگر نقطه از -> استفاده می شود.همانند اشاره گر استراکچر ها که قبلا بیان شد.

2.مفاهیم اشاره گر های کلاس ها همان مفاهیم انواع داده ای استراکچر ها می باشد که در جلاسات آینده بیان می شود.

 

خب بر می گریم به اجرا ادامه ی برنامه. خب خط نهم مشخصه مشابه خط هفتم هست که این بار با توجه به معکوس شدن عددn1مقدار معکوس عدد چاپ شده توسط خط هفتم رو چاپ می کند.در خط بعد تابع Addعضو شئn1فراخوانی می شود این تابع خودش آرگمانی از نوع کلاس عدد کسری دارد.از طریق این آرگمان یک کپی از شئ n2را به تابع مذکور می فرستیم.سپس این تابع کسر n2را n1جمع می کند و نتیجه را در n1می ریزد. در واقع شی فراخوانی کننده رابا آرگمان خود جمع می کند.

چند خط بعد هم همینطوره فقط این بار n2خودش رو با n1جمع می کنه.

 

نکته:

یک کلاس فقط یک تعریف است و وجود خارجی ندارد بنابراین هنگام تعریف قالب یک کلاس نمی توانیم ضمن تعریف به فیلد ها مقدار اولیه بدهیم مثل تعریف زیر که خطای کامپایلری دارد.

class MyClass{

public:

int a=0,b=1;

float GetAsFloat();

void InverseIt();

void Add(MyClass tmp);

};

برای حل این مشکل تابعی به نام تابع سازنده وجود دارد این تابع هم نام کلاس است و هیچ گونه نوع باز گشتی حتیvoidندارد. به تعریف زیر دقت کنید

class MyClass{

public:

تعریف الگوی تابعMyClass();

int a,b;

float GetAsFloat();

void InverseIt();

void Add(MyClass tmp);

};

تعریف بدنه ی تابع سازنده

MyClass::MyClass(){

a=0; b=1;

}

تابع سازنده هر زمان که یک شئ ساخته می شود برای شئ در حال ایجاد فراخوانی می شود و کار های لازم برای مقدار دهی اولیه به شئ مذکور انجام می دهد. توجه داشته باشید که  شئ مذکور چه با عملگرnew  ساخته شود یا به طور مستقیم در برنامه تعریف شود (همانطور که n1,n2تعریف شدند)تابع سازنده فراخونی می شود در واقع شئ ایجاد نمی شود مگر که تابع سازنده در صورت وجود فراخوانی شود.

کاربرد تابع سازنده وقتی جالب تر می شود که بخواهیم مقادیری غیر از مقادیر از قبل تعریف شده به اعضا کلاس بدهیم! به مثال توجه کنید.

 

class MyClass{

public:

تعریف الگوی تابعMyClass();

MyClass(int tmpa, int tmpb);

int a,b;

float GetAsFloat();

void InverseIt();

void Add(MyClass tmp);

};

تعریف بدنه ی تابع سازنده

MyClass::MyClass()

{

a=0; b=1;

 }

تعریف بدنه ی تابع سازنده

MyClass::MyClass(int tmpa, int tmpb)

{

a=tmpa; b=tmpb;

}

در این مثال دو تابع برای تابع سازنده تعریف کردیم همانطور که در مبحث توابع گفتیم به این عمل overloadمی گویند و کامپایر از روی نوع و تعداد آر گمان ها تشخیص می دهد کدام نسخه از تابع را اجرا کند.

حال بااین تعریف می توانیم شئ n1را به صورت

MyClass n1(2,3);

تعریف کنیم ضمن اینکه به همون صورت قبل هم میشه این کارو انجام داد

MyClass n1;

جالب تر این که وقتی یه تابع سازنده با که فقط یک آرگمان داره داشته باشیم می تونیم شئ مورد نظر رو به صورت های

MyClass n1(2);

یا

MyClass n1=2;

یعنی اینکه بجای پرانتز می شه از مساوی هم استفاده کرد! البته فقط در صورت وجود تابع سازنده با یک آرگمان.مثلا برای این کلاس می تونیم یه تابع سازنده بسازیم که صورت کسر رو از برنامه بگیره و مخرج رو به صورت پیش فرض یک مقدار دهی کنه در این صورت کلاسمون اعداد صحیح رو هم به عنوان مقدار اولیه قبول می کنه.

خب دیگه زیاد شد!

جلسه بعد را جع به تابع مخرب و اعضا خصوصی و عمومی(public,private)صحبت می کنیم

.تا پایان مباحث شئ گرایی سعی می کنیم همین کلاس اعداد کسری رو کامل کنیم طوری که بتونیم به راحتی کار با انواع اولیه با هاش کارکنیم.

+ نوشته شده توسط سجاد مهدی بیرقدار در دوشنبه یکم بهمن 1386 و ساعت 1:13 |