تبليغاتX
C++ آموزش - جلسه ی پانزدهم(مروری بر مباحث قبل و مقدمات شئ گرایی)

تعریف:

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

 

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

int fact(int n){

 if(n<=1)

    return 1;

 return fact(n-1)*n;

}

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

 

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

void transfer(int n,Bar from,Bar to,Bar temp){

if(n>0){

 transfer(n-1,from,temp,to);

 msg( from, to);

 MoveDisk(from,to);

 transfer(n-1,temp,to,from);

 }

}

اولین آرگمان این تابع عددی صحیح نمایانگر تعداد حلقه ها و سه آرگمان دیگر میله هایی هستند که به ترتیب باید یک حلقه آز آن برداشته شود(from)مقصد حلقه(to) محل قرار گیری موقت(temp). نوع داده ای این سه آرگمان یک داده ی شمارشی است

 

تعریف:

داده های شمارشی داده هایی هستند که مقادیر خاصی را که برنامه نویس به صورت عباراتی مشخص میکند  ذخیره میکنند و در طول برنامه با این عبارات مانند اعداد صحیح برخورد می شود.در حقیقت اعداد به عبارات نوع داده ی شمارشی نسبت داده می شوند.

enum Bar{L,C,R};//L=0, C=1, R=2

در این مسائله نوع دادای Barرا تعریف کردیم که مقادیر L C Rنمایانگر میله ها ی سمت راست ,چپ و مرکز است که در واقع مقادیر صحیح صفر تا دو هستند.

 

حال برای مثال دستوری مانند

transfer(5,L,R,C);

به معنی انتقال دیسک 5از میله ی چپ به راست است آنهم بااستفاده از میله مرکزی برای مکان موقت. با اجرای این دستور تابع به نوبه ی خود دستور

transfer(n-1,from,temp,to);

که معادل

transfer(4,L,C,R);

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

transfer(3,L,R,C);

اجرا می کند واین روند ادامه دارد تا جایی که nبه صفر می رسد

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

مثلا برای پیاده سازی این مسئله باید کلیت آن را پیاده سازی کرد یعنی همان منطقی که در ابتدا بیان شد.و نیاز به فکر در مورد چگونگی انتقال تک به تک دیسک ها نیست.

 

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

توابع msqوMoveDiskهم فقط وضعیت جاری مسئله را برای ترسیم صفحه نمایش از تابع transferمی گیرند حال می خواهیم به بررسی کلی برنامه بپردازیم و به این نتیجه برسیم که شی گرایی چقدر کد نویسی را زیبا و قابل درک میکند!

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

struct disk{int Size,Color;};

تعریف شده که رنگ و اندازه ی هر دیسک را ذخیره می کند.

استراکچر دیگری به صورت

struct stack{int i; disk *Disks;};

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

 

تعریف:

stackساختمان داده ای است که در هر دسترسی به داده های(در این جا دیسک ها) ذخیره شده در آن فقط می توان داده ای را به انتهای آن اضافه کرد(در اینجا دیسکی روی میله قرار داد) یا آخرین داده را از انتهای آن خواند(در اینجا دیسکی از روی میله برداشت)

فیلد Disksدر این استراکچر برای ذخیره ی اشاره گری به آرایه ای از استراکچر   diskکه در واقع محتوی حلقه های روی میله است تعریف شده و فیلد iبرای ذخیره ایندکس  آخرین حلقه است.

در این برنامه آرایه ای سه عنصری برای ذخیره ی وضعیت سه میله با حلقه هایشان تعریف شده

stack Bars[3]={ {0,{0}} ,{0,{0}}, {0,{0}} };

دیگر توابع مهم برنامه تابع MoveDiskاست که بعد از جابجا کردن دیسکی از یک میله به میله ی دیگر تابع DrawBars را فراخوانی می کند این تابع به نوبه ی خود محتویات صفحه نمایش را براساس محتویات آرایه ی Barsترسیم می کند. اگر تا کنون متوجه شده باشید می بینیم که برای فهم کامل یک برنامه ی اینچنینی که در واقع برنامه ای ساخت یافته است لازم است تک به تک توابع برنامه را بشناسیم و بادنیای عظیمی از توابع آشنا شویم. بزرگترین مشکل ساخت یافتگی همین است مشکل دیگر اینکه اگر متغییر های این برنامه به صورت عمومی تعریف نشده بود مجبور بودیم برای توابع برنامه آرگمان های گوناگونی در نظر بگیریم و مسائلی از این دست.اما آیا؟

1.آیا واقعا نیازی هست که با این همه توابع برای وظایف گوناگون در گیر باشیم؟

2.آیا برای اینکه یک میله با قابلیت های سه میله ی این مسئله در برنامه داشته باشیم نیازی به دانستن اینکه این میله ها چگونه و توسط چه تابعی رسم می شوند لازم است؟

3.آیا لازم است بدانیم این میله ها چگونه و به وسیله ی چه ساختمان داده ای دیسک هایش را ذخیره میکند؟

خیر جواب همه ی این سوالات منفی است!

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

خب به همین سادگی شی گرایی دقیقا همین است بسته بندی توابع و متغیر ها و بعد پنهان کردن قسمت هایی که ما نباید ببینیم! در هنگام نوشتن دستورات برنامه ,نمود اشیا به صورت یک نام است.همانطور که نمود متغیر ها به صورت یک نام است.

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

پس اشیا بسته هایی از توابع و متغیرها هستند که با کمک هم کاری را انجام می دهند.

نقشه ی ساخت اشیا تحت عنوان یک کلاس تعریف می شود این کلاس خود نامی دارد که برای ساخت اشیا از این کلاس باید به کامپایلر بگوییم که یک شی به نام مثلا objاز کلاس مثلا 1class بساز که class1نام کلاس است. که دستور آن به شکل زیر است

class1 obj;

همان طور که می بینید این دستور خیلی شبیه دستور زیر است

int a;

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

 

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

 

+ نوشته شده توسط سجاد مهدی بیرقدار در چهارشنبه بیست و سوم آبان 1386 و ساعت 12:34 |