برنامه نویسی ++C
مقایسه C و C++:
اگر بخواهید یک اتومبیل را مدل کنید، اتومبیل یک سری خصوصیات دارد مثل رنگ بدنه، ، طول، عرض، حداکثر سرعت، میزان مصرف بنزین و ... از طرفی دیگر، اتومبیل و اجزای آن برای ما کار هایی انجام می دهند. مثلا با زدن شصتی دزدگیر، قفل مرکزی قفل را باز می کند. با رفتن به درون خودرو و استارت زدن، موتور روشن می شود. دسته کنار فرمان چراغ ها را روشن می کند، پدال گاز سرعت ماشین را بیشتر می کند و ... پس دو چیز وجود دارد. یکی خصوصیات یک شی و دیگری اعمالی که یک شی برای ما انجام می دهد.
حال اگر بخواهید با C یک پژو پارس را مدل کنید. پژو پارس از اشیاء مختلفی تشکیل شده است. برای مثال مانیفلد. از خصوصیات مانیفلد، قطر دهانه هوای ورودی آن است. این را باید با یک متغییر نشان می دهیم. کاری که مانیفلد انجام می دهد یعنی رساندن حجم هوای مناسب به انژکتور را هم با یک تابع مدل می کنیم. ملاحظه می کنید که خصوصیات مانیفلد و کاری که انجام می دهد از هم جدا هستند. در یک برنامه بزرگ برنامه نویس به راحتی این ها را با هم قاطی کرده و جایگاه هیچ چیز معلوم نیست. ضمن اینکه اگر بخواهیم یک ماشین دیگر مثلا 206 را مدل کنیم، یک سری توابع را باید دوباره از اول بنویسیم و متغییر هایی را از اول تعریف کنیم. در یک کلام هیچ سازمان دهی و نظم منسجمی در کد ما نیست و کد ما بی نظم است.
حال وارد C++ می شویم. در اینجا سیستم را از بالا به پایین نظم می دهیم. وجه مشترک 206 و پژو پارس اتومبیل بودن آن ها است. اتومبیل یک کلاسی است که همه اتومبیل ها حتی بوگاتی ویرن زیر مجموعه این کلاس هستند. این کلاس خصوصیات و عملکرد هایی دارد که در همه نمونه ها وجود دارد. مثل سرعت.
در کلاس، خصوصیات (متغیر ها) و عملکرد ها (توابع) با یک نام مشترک شناخته می شوند. یعنی دیگر از هم جدا نیستند و همه خواص و عملکرد های یک شی با یک نام شناخته می شوند. دسترسی ها به متغییر ها و توابع کلاس نیز کنترل شده است که بعدا خواهد آمد.
اگر به 206 بصورت یک شی نگاه کنیم. این شی شامل اشیاء دیگری است. مثلا چرخ، شیشه، موتور و ... هرکدام از این اشیاء خصوصیات و عملکرد هایی دارد. مثلا هر چرخی چه گلدستون، چه میشلن، چه بریجستون و ... جزء کلاس چرخ محسوب می شوند. شما با اضافه یا کم کردن خواص و عملکرد های کلاس چرخ می توانید به شی گلدستون برسید.
پس ما یک کلاس کلی تعریف می کنیم که خصوصیات مشترک همه را داشته باشد، بعد از آن کلاس هایی با جزییات بیشتر می سازیم و تفاوت ها را لحاظ می کنیم تا به شی مورد نظر برسیم. مثلا در مثال بالا 3 کلاس مختلف برای 3 کمپانی مشهور تایر خواهیم داشت و در نهایت چرخ روی ماشین بقالی سر کوچمون یک شی خاص از یکی از این کلاس ها خواهد بود. تحقق نهایی یک کلاس در خارج، یک شی از آن کلاس است. جایی که دیگر از انتزاع خارج شده ایم.
تعریف کلاس:
یک کلاس با کلمه کلیدی class و نام آن تعریف می شود. ضمنا به عملکرد ها یا توابع کلاس متد گفته می شود.
} نام کلاس class
خاصیت ها(متغییر ها)
.
.
متدها (توابع)
;{
خواص و متد های یک کلاس از نظر حوزه دید به 3 دسته تقسیم می شوند:
- private: خواص و متدهایی که به صورت private تعریف شده باشند، فقط در داخل متد های خود کلاس قابل دسترسی اند. یعنی اگر یک شی از کلاس تعریف کردید، در برنامه کسی به آن ها دسترسی ندارد. (در مثال ها خواهد آمد)
- public: خواص و متد هایی که به صورت public تعریف شده اند، هم داخل متد های کلاس و هم داخل برنامه قابل دسترسی اند.
- protected: خواص و متد هایی که به صورت protected تعریف شده باشند، فقط در داخل متدهای خود کلاس یا کلاسی که از آن ارث می برد قابل دسترسی هستند.
مثال 1: می خواهیم کلاس 2 بعدی را مدل کنیم. هر شی دو بعدی یک طول دارد یک عرض(خواص) پس با دو متغییر آن ها را نشان می دهیم. متد هایی هم باید باشند که مساحت و محیط شی دو بعدی را برای ما محاسبه کرده، نشان دهند. در دو بعد ما مثلا مستطیل و مثلث داریم که دو جور محیط و مساحت دارند. پس درحالت عادی دو متد ما بدون بدنه می شود. البته راه درست است. اما از بحث مقاله خارج است. به جای این کار ما کلاس را فقط برای مستطیل کامل می کنیم.
?
12345678910111213141516171819202122232425262728293031 | class Rec{ private: int length; int width; public: int get_dim(int l, int w){ length = l; width = w; } int sphere(){ return (width + length)*2; } int area(){ return width*length; } }; |
دلیل اینکه دو متغییر width و lenhgt را private تعریغ کردیم این بود که لازم نبود در برنامه دیده شوند. اگر می خواهید در برنامه دیده شوند، آن ها را در قسمت public تعریف کنید. این کلاس 3 متد دارد که اولی طول و عرض را می گیرد. و با دو متد دیگر محیط و مساحت مستطیل را محاسبه می کند. توجه کنید متد get_dim می تواند متغییر های خصوصی طول و عرض را مقدار دهی کند. زیرا داخل خود کلاس دسترسی کامل داریم.
در ادامه مثال می بینیم یک کلاس چگونه در برنامه تعریف می شود. بدنه کلاس مثل مثال بالا را باید قبل از main تعریف کنید. در فایلی جداگانه نیز می شود تعریف کرده و بعد include کرد. داخل main برنامه باید یک شی از کلاس به صورت زیر با نام دلخواه تعریف کنید.
?
12345678910111213 | int Main(){ Rec obj1; obj1.get_dim(2, 5); cout << “the sphere is:” << obj1.sphere() << endl; cout << “the area is:” << obj1.area() << endl; return 0; } |
ابتدا یک شی به نام obj1 از کلاس Rec تعریف شده است در ادامه به دو بعد توسط متد get_dim مقدار 2 و 5 برای طول و عرض داده شده. برای دسترسی به خواص و متد های یک شی از علامت نقطه استفاده می کنیم. دو خط آخر محیط و مساحت را که به ترتیب برابر 14 و 10 است را در خروجی نشان می دهد. توجه کنید که ما در تابع main به متغییر های خصوصی دسترسی نداریم و اگر تلاش کنید دسترسی پیدا کنید کامپایلر خطا می دهد. ولی 3 تابع اصلی کلاس که publicبودند در main فراخوانی شده اند.
مثال 2: در این مثال روش دیگری را برای تعریف کلاس در تابع main برسی می کنیم و از همان کلاس Rec مثال اول استفاده می کنیم.
?
12345678910111213 | int main(){ Rec *obj2 = new Rec; obj2 -> get_dim(6, 4); cout << “the sphere is:” << obj2 -> shere() << endl; cout << “the erea is:” << obj2 -> area() << endl; return 0; } |
این برنامه عدد 20 و 24 را بعنوان محیط و مساحت نشان می دهد. این بار در تعریف کلاس از یک اشاره گر و کلمه کلیدیnew استفاده کرده ایم. عبارت new Rec (عبارت سمت راست) مقداری به اندازه کلاس در حافظه ایجاد می کند و اشاره گرobj2 به این محل که نمی دانیم در حافظه کجاست اشاره می کند. تنها راه دسترسی به خصوصیات و متد های کلاس این اشاره گر است. در این حالت برای دسترسی به خواص و متد های کلاس بجای علامت . (نقطه) از علامت -> استفاده می شود. هرکدام از دو روش تعریف کلاس کاربردی مختص به خود را دارد.
سازنده کلاس:
گاهی اوقات لازم است که خواص کلاس قبل از تعریف شدن مقدار دهی اولیه شود یا متدی اجرا شود تا در زمان اجرا به مشکل بر نخوریم. این کار را سازنده کلاس انجام می دهد و این کار را در همان خط تعریف کلاس انجام می دهد.
سازنده باید حتما همنام کلاس باشد. حال دو باره کلاس Rec را با سازنده تعریف می کنیم. در سازنده مقدار اولیه طول و عرض را برابر 3 قرار می دهیم.
?
123456789101112131415161718192021222324252627282930313233343536373839 | class Rec{ private: int length; int width; public: Rec(){ length = 3; width = 3; } int get_dim(int l, int w){ length = l; width = w; } int sphere(){ return (width + length)*2; } int area(){ return width*length; } }; |
می بینید که سازنده نوع (void, int, …) ندارد و یک سازنده ساده تقریبا همیشه باید public تعریف شود.
در خط تعریف کلاس دو خاصیت طول و عرض مقدار اولیه 3 را می گیرد. اما اجازه دهید تا بتوانیم در زمان تعریف کلاس هم بتوانیم طول و عرض را تعیین کنیم. این کار را با سازنده انجام می دهیم.
مثال 3:
?
123456789101112131415161718192021222324252627282930313233343536373839 | class Rec{ private: int length; int width; public: Rec(int l, int w){ length = l; width =w; } int get_dim(int l, int w){ length = l; width =w; } int sphere(){ return (width + length)*2; } int area(){ return width*length; } }; |
و main برنامه هم به صورت زیر می شود:
?
1234567891011 | int main(){ Rec obj3(1, 7); cout << “the sphere is:” << obj3.shere() << endl; cout << “the erea is:” << ob3.area() << endl; return 0; } |
و در شیوه دیگر تعریف کلاس همین برنامه به صورت زیر می شود.
?
1234567891011 | int main(){ Rec obj4 = new Rec(1, 7); cout << “the sphere is:” << obj4->shere() << endl; cout << “the erea is:” << ob4->area() << endl; return 0; } |
هردو برنامه عدد 16 را بعنوان محیط و عدد 7 را بعنوان مساحت نمایش می دهند.
وراثت:
کلاس ها می تواند از هم ارث ببرند. کلاس فرزند خواص و متد های public یا protected کلاس والد را به ارث می برد. مثلا اگر بخواهیم کلاس مکعب مستطیل را بسازیم. بجای اینکه از اول کلاس را بنویسیم، از کلاس مستطیل که نوشته ایم استفاده می کنیم. اگر کلاس مکعب مستطیل از کلاس مستطیل ارث ببرد، کلاس مکعب مستطیل همه متد های کلاس مستطیل را به ارث می برد و فقط کافی است از این متد ها در کلاس جدید استفاده کنیم.
مکعب مستطیل دارای طول، عرض و ارتفاع است. پس فقط یک ارتفاع زیادتر داریم. متد ها نیز مساحت و حجم است. می توان متد مساحت را با کلاس والد هم نام گرفت ولی از بحث مقاله خارج است و ما نامی دیگر برای مساحت انتخاب می کنیم. ضمنا دو متغییر خصوصی کلاس مستطیل را هم protected می کنیم تا در کلاس فرزند (مکعب مستطیل) قابل دسترس باشد.
آینده نگری بیشتر اوقات کمک می کند. اگر ما از اول در پی گسترش کار و کلاس های خود باشیم، طبق آینده نگری کار کرده و خیلی مشکلات کم می شود. می توانستیم از همان ابتدا متغییر های طول و عرض را در کلاس مستطیل protected تعریف کنیم.
مثال 4: اکنون ابتدا کلاس مستطیل را به شکل جدید دوباره نویسی می کنیم.
?
123456789101112131415161718192021222324252627282930313233343536373839 | class Rec{ protected: int length; int width; public: Rec(int l, int w){ length = l; width =w; } int get_dim(int l, int w){ length = l; width =w; } int sphere(){ return (width + length)*2; } int area(){ return width*length; } }; |
حال کلاس مکعب مستطیل را می نویسیم:
?
123456789101112131415161718192021222324252627282930313233343536373839 | class Cub : Rec{ protected: int heigth; public: Cub(int l, int w, int h){ length = l; width = w; height =h; } int get_dim3(int l, int w, int h){ length = l; width = w; height = h; } int area3(){ return (area()*2 + sphere()*height) ; } int volume(){ return area()*height; }; |
خط اول نشان می دهد کلاس Cub از کلاس Rec ارث می برد. مشاهده می کنید که خصوصیات و متد های کلاس Rec در کلاس Cub در دسترس هستند.
حال main برنامه:
?
12345678910111213 | int main(){ Cub obj4(1, 9, 3); cout << “the area is:” << obj4.area3() << endl; cout << “the volume is:” << obj4.volume() << endl; cout << “the area of frame is: << obj4.area() << endl; return 0; } |
این برنامه به ترتیب عدد 52 را برای مساحت، عدد 27 را برای حجم و عدد 9 را برای مساحت قاعده مکعب مستطیل چاپ می کند. توجه کنید که عدد آخر را توسط متد کلاس والد (Rec) حساب کردیم. زیرا همان
نطور که گفته شد، خصوصیات و متد هایpublic یا protected کلاس والد در کلاس فرزند قابل دسترسی است.