برنامه سازی پیشرفته ++C
در نمایش آنلاین پاورپوینت، ممکن است بعضی علائم، اعداد و حتی فونتها به خوبی نمایش داده نشود. این مشکل در فایل اصلی پاورپوینت وجود ندارد.
- جزئیات
- امتیاز و نظرات
- متن پاورپوینت
برچسبهای مرتبط
- برنامه سازی
- برنامه سازی پيشرفته
- برنامه نویسی
- پاورپوينت برنامه سازی پيشرفته ++C
- پاورپوینت
- پاورپوینت آماده
- پاورپوینت رایگان
- تاریخچه C++
- حوزه متغيرها
- دانلود پاورپوینت
- دانلود پاورپوینت آماده
- دانلود پاورپوینت رایگان
- زبان #C
- زبان برنامه نویسی
- زبان برنامه نویسی C++
- عملگر خروجی
- عملگر ورودی
- عملگرهای مقدارگذاری مرکب
- کاراکترها
- ليترال ها
- مقداردهی اوليه
- مقدارگذاری مرکب
- نحو اعلان يک متغير
امتیاز
برنامه سازی پیشرفته ++C
اسلاید 1: بسم الله الرحمن الرحيم
اسلاید 2: دانشگاه پيام نوردانشكده فناوري اطلاعات
اسلاید 3: تهيه كننده: دكتر احمد فراهيبرنامه سازي پيشرفته
اسلاید 4: زبان C يک زبان همه منظوره است. دستورالعملهاي اين زبان بسيار شبيه عبارات جبري و نحو آن شبيه جملات انگليسي مي باشد. اين امر سبب ميشود که C يک زبان سطح بالا باشد که برنامهنويسي در آن آسان است ›››مقدمه:
اسلاید 5: ++C که از نسل C است، تمام ويژگيهاي C را به ارث برده است. اما برتري فني ديگري هم دارد: C++ اکنون «شيگرا» است. ميتوان با استفاده از اين خاصيت، برنامههاي شيگرا توليد نمود. برنامههاي شيگرا منظم و ساختيافتهاند، قابل روزآمد کردناند، به سهولت تغيير و بهبود مييابند و قابليت اطمينان و پايداري بيشتري دارند.
اسلاید 6: اهم مطالب اين كتاب :جلسه سوم: «انتخاب»جلسه دوم: «انواع اصلي»جلسه پنجم: «توابع»جلسه چهارم: ‹‹تكرار»جلسه اول: «مقدمات برنامهنويسي با C++»جلسه ششم: « آرايهها»
اسلاید 7: جلسه نهم: «شيئگرايي»جلسه هشتم: «رشتههاي كاراكتري و فايلها در ++Cاستاندارد»جلسه دهم: «سربارگذاري عملگرها» جلسه هفتم: «اشارهگرها و ارجاعها»جلسه يازدهم: «تركيب و وراثت»
اسلاید 8: جلسه اولمقدمات برنامهنويسي با C++
اسلاید 9: آنچه در اين جلسه مي خوانيد:1- چرا C++ ؟2- تاريخچۀ C++3- آمادهسازي مقدمات4- شروع کار با C++5- عملگر خروجي6- ليترالها و کاراکترها7- متغيرها و تعريف آنها8- مقداردهي اوليه به متغيرها9- ثابتها10- عملگر ورودي
اسلاید 10: هدف کلي: آشنايي با تاريخچه و مزاياي زبان برنامهنويسي C++ و بيان مفاهيم بنيادي شيگرايي و عناصر مهم برنامههاي C++
اسلاید 11: هدفهاي رفتاري: انتظار ميرود پس از پايان اين جلسه بتوانيد:- مزاياي زبان C++ را بر زبانهاي مشابه ذکر کرده و تفاوت آن را با زبان C بيان کنيد.- شرح مختصري از روند پيشرفت زبانهاي برنامهنويسي را بيان کرده و مشکلات هر دوره را به اختصار شرح دهيد.- مزاياي شيگرايي در توليد نرمافزار را برشماريد.- اصول سهگانۀ شيگرايي را نام برده و هر يک را به اختصار شرح دهيد.>>
اسلاید 12: - قالب کلي برنامههاي C++ را بشناسيد و بتوانيد برنامههاي کوچک را نوشته و آزمايش کنيد.- عملگر ورودي و خروجي را در C++ شناخته و از آنها در برنامهها استفاده کنيد.- نحوۀ اعلان متغيرها و شيوۀ مقداردهي به آنها را بدانيد.- سه موجوديت «ليترال»، «کاراکتر» و «عدد» را شناخته و فرق بين آنها را شرح دهيد.- علت و شيوههاي افزودن توضيح به کد برنامه را شرح دهيد.- علت و شيوۀ معرفي ثابتها در برنامه را شرح دهيد.
اسلاید 13: مقدمهدر دهه 1970 در آزمايشگاههاي بل زباني به نام C ايجاد شد. انحصار اين زبان در اختيار شرکت بل بود تا اين که در سال 1978 توسط Kernighan و Richie شرح کاملي از اين زبان منتشر شد و به سرعت نظر برنامهنويسان حرفهاي را جلب نمود. هنگامي که بحث شيگرايي و مزاياي آن در جهان نرمافزار رونق يافت، زبان C که قابليت شيگرايي نداشت ناقص به نظر ميرسيد تا اين که در اوايل دهۀ 1980 دوباره شرکت بل دست به کار شد و Bjarne Stroustrup زبان C++ را طراحي نمود
اسلاید 14: C++ ترکيبي از دو زبان C و Simula بود و قابليتهاي شيگرايي نيز داشت. از آن زمان به بعد شرکتهاي زيادي کامپايلرهايي براي C++ طراحي کردند. اين امر سبب شد تفاوتهايي بين نسخههاي مختلف اين زبان به وجود بيايد و از قابليت سازگاري و انتقال آن کاسته شود. به همين دليل در سال 1998 زبان C++ توسط موسسۀ استانداردهاي ملي آمريکا (ANSI) به شکل استاندارد و يکپارچه درآمد.
اسلاید 15: 1- چرا C++ ؟زبان C يک زبان همه منظوره است در اين زبان عملگرهايي تعبيه شده که برنامهنويسي سطح پايين و به زبان ماشين را نيز امکانپذير ميسازد چون C عملگرهاي فراواني دارد، کد منبع برنامهها در اين زبان بسيار کوتاه است
اسلاید 16: برنامۀ مقصدي که توسط کامپايلرهاي C ساخته ميشود بسيار فشردهتر و کمحجمتر از برنامههاي مشابه در ساير زبانها است.C++ که از نسل C است، تمام ويژگيهاي جذاب C را به ارث برده است .و سرانجام آخرين دليل استفاده از C++ ورود به دنياي C# است. - زبان C براي اجراي بسياري از دستوراتش از توابع کتابخانهاي استفاده ميکند و بيشتر خصوصيات وابسته به سختافزار را به اين توابع واگذار مينمايد.
اسلاید 17: 2- تاريخچۀ C++ در دهه 1970 در آزمايشگاههاي بل زباني به نام C ايجاد شد. انحصار اين زبان در اختيار شرکت بل بود تا اين که در سال 1978 توسط Kernighan و Richie شرح کاملي از اين زبان منتشر شد و به سرعت نظر برنامهنويسان حرفهاي را جلب نمود. هنگامي که بحث شيگرايي و مزاياي آن در جهان نرمافزار رونق يافت، زبان C که قابليت شيگرايي نداشت ناقص به نظر ميرسيد تا اين که در اوايل دهۀ 1980 دوباره شرکت بل دست به کار شد و Bjarne Stroustrup زبان C++ را طراحي نمود.
اسلاید 18: C++ ترکيبي از دو زبان C و Simula بود و قابليتهاي شيگرايي نيز داشت از آن زمان به بعد شرکتهاي زيادي کامپايلرهايي براي C++ طراحي کردند. اين امر سبب شد تفاوتهايي بين نسخههاي مختلف اين زبان به وجود بيايد و از قابليت سازگاري و انتقال آن کاسته شود. به همين دليل در سال 1998 زبان C++ توسط موسسۀ استانداردهاي ملي آمريکا (ANSI) به شکل استاندارد و يکپارچه درآمد. کامپايلرهاي کنوني به اين استاندارد پايبندند. کتاب حاضر نيز بر مبناي همين استاندارد نگارش يافته است.
اسلاید 19: 3- آمادهسازي مقدمات يک «برنامه» دستورالعملهاي متوالي است که ميتواند توسط يک رايانه اجرا شود. براي نوشتن و اجراي هر برنامه به يک «ويرايشگر متن» و يک «کامپايلر» احتياج داريم. بستۀ Visual C++ محصول شرکت ميکروسافت و بستۀ C++ Builder محصول شرکت بورلند نمونههاي جالبي از محيط مجتمع توليد براي زبان C++ به شمار ميروند.
اسلاید 20: 4- شروع کار با C++C++ نسبت به حروف «حساس به حالت» است يعني A و a را يکي نميدانداولين برنامهاي که مينويسيم به محض تولد، به شما سلام ميکند و عبارت Hello, my programmer! را نمايش ميدهد:#include <iostream>int main(){ std::cout << Hello, my programmer!n ; return 0;} مثال : اولين برنامه
اسلاید 21: اولين خط از کد بالا يک «راهنماي پيشپردازنده» است. راهنماي پيشپردازنده شامل اجزاي زير است: 1- کاراکتر # که نشان ميدهد اين خط، يک راهنماي پيشپردازنده است. اين کاراکتر بايد در ابتداي همۀ خطوط راهنماي پيشپردازنده باشد.2- عبارت include3- نام يک «فايل کتابخانهاي» که ميان دو علامت <> محصور شده است.
اسلاید 22: خط دوم برنامه نيز بايد در همه برنامههاي C++ وجود داشته باشد. اين خط به کامپايلر ميگويد که «بدنۀ اصلي برنامه» از کجا شروع ميشود. اين خط داراي اجزاي زير است:1 – عبارت int که يک نوع عددي در C++ است. 2 – عبارت main که به آن «تابع اصلي» در C++ ميگويند.3 – دو پرانتز () که نشان ميدهد عبارت main يک «تابع» است. هر برنامه فقط بايد يک تابع main() داشته باشد .
اسلاید 23: سه خط آخر برنامه، «بدنۀ اصلي برنامه» را تشکيل ميدهند. دستورات برنامه از خط سوم شروع شده است. دستور خط سوم با علامت سميکولن ; پايان يافته است.
اسلاید 24: توضيح توضيح، متني است که به منظور راهنمايي و درک بهتر به برنامه اضافه ميشود و تاثيري در اجراي برنامه ندارد. . کامپايلر توضيحات برنامه را قبل از اجرا حذف ميکند. استفاده از توضيح سبب ميشود که ساير افراد کد برنامۀ شما را راحتتر درک کنند.
اسلاید 25: به دو صورت ميتوانيم به برنامههاي C++ توضيحات اضافه کنيم: 1 – با استفاده از دو علامت اسلش // : هر متني که بعد از دو علامت اسلش بيايد تا پايان همان سطر يک توضيح تلقي ميشود .2 – با استفاده از حالت C : هر متني که با علامت /* شروع شود و با علامت */ پايان يابد يک توضيح تلقي ميشود.
اسلاید 26: 5- عملگر خروجيعلامت << عملگر خروجي در C++ نام دارد (به آن عملگر درج نيز ميگويند). يک «عملگر» چيزي است که عملياتي را روي يک يا چند شي انجام ميدهد. عملگر خروجي، مقادير موجود در سمت راستش را به خروجي سمت چپش ميفرستد. به اين ترتيب دستور cout<< 66 ;مقدار 66 را به خروجي cout ميفرستد که cout معمولا به صفحهنمايش اشاره دارد. در نتيجه مقدار 66 روي صفحه نمايش درج ميشود.
اسلاید 27: 6 -ليترالها و کاراکترهايک «ليترال» رشتهاي از حروف، ارقام يا علايم چاپي است که ميان دو علامت نقل قول محصور شده باشد. يک «کاراکتر» يک حرف، رقم يا علامت قابل چاپ است که ميان دونشانۀ محصور شده باشد. پس w و ! و 1 هر کدام يک کاراکتر است. به تفاوت سه موجوديت «عدد» و «کاراکتر» و «ليترال رشتهاي» دقت کنيد: 6 يک عدد است، 6 يک کاراکتر است و 6 يک ليترال رشتهاي است.
اسلاید 28: 7 - متغيرها و تعريف آنها:«متغير» مکاني در حافظه است که چهار مشخصه دارد: نام، نوع، مقدار، آدرس. وقتي متغيري را تعريف ميکنيم، ابتدا با توجه به نوع متغير، آدرسي از حافظه در نظر گرفته ميشود، سپس به آن آدرس يک نام تعلق ميگيرد.
اسلاید 29: در C++ قبل از اين که بتوانيم از متغيري استفاده کنيم، بايد آن را اعلان نماييم. نحو اعلان يک متغيرtype name initializerعبارت type نوع متغير را مشخص ميکند. نوع متغير به کامپايلر اطلاع ميدهد که اين متغير چه مقاديري ميتواند داشته باشد و چه اعمالي ميتوان روي آن انجام داد.
اسلاید 30: name initializerعبارت name نام متغير را نشان ميدهد. اين نام حداکثر ميتواند 31 کاراکتر باشد، نبايد با عدد شروع شود، علايم رياضي نداشته باشد و همچنين «کلمۀ کليدي» نيز نباشد. عبارت initializer عبارت «مقداردهي اوليه» نام دارد. با استفاده از اين عبارت ميتوان مقدار اوليهاي در متغير مورد نظر قرار داد.مقداردهي اوليه دستور زير تعريف يک متغير صحيح را نشان ميدهد:int n = 50;
اسلاید 31: 8 - مقداردهي اوليه به متغيرهادر بسياري از موارد بهتر است متغيرها را در همان محلي که اعلان ميشوند مقداردهي کنيم. استفاده از متغيرهاي مقداردهي نشده ممکن است باعث ايجاد دردسرهايي شود. دردسر متغيرهاي مقداردهي نشده وقتي بزرگتر ميشود که سعي کنيم متغير مقداردهي نشده را در يک محاسبه به کار ببريم. مثلا اگر x را که مقداردهي نشده در عبارت y = x + 5; به کار ببريم، حاصل y غير قابل پيشبيني خواهد بود. براي اجتناب از چنين مشکلاتي عاقلانه است که متغيرها را هميشه هنگام تعريف، مقداردهي کنيم.مثال: int x=45; int y=0;
اسلاید 32: 9- ثابتهادر بعضي از برنامهها از متغيري استفاده ميکنيم که فقط يک بار لازم است آن را مقداردهي کنيم و سپس مقدار آن متغير در سراسر برنامه بدون تغيير باقي ميماند. مثلا در يک برنامۀ محاسبات رياضي، متغيري به نام PI تعريف ميکنيم و آن را با 3.14 مقداردهي ميکنيم و ميخواهيم که مقدار اين متغير در سراسر برنامه ثابت بماند. در چنين حالاتي از «ثابتها» استفاده ميکنيم. يک ثابت، يک نوع متغير است که فقط يک بار مقداردهي ميشود و سپس تغيير دادن مقدار آن در ادامۀ برنامه ممکن نيست. تعريف ثابتها مانند تعريف متغيرهاست با اين تفاوت که کلمه کليدي const به ابتداي تعريف اضافه ميشود.
اسلاید 33: int main(){ // defines constants; has no output: const char BEEP =b; const int MAXINT=2147483647; const float DEGREE=23.53; const double PI=3.14159265358979323846 return 0;}برنامه فوق خروجي ندارد:مثال تعريف ثابتها:
اسلاید 34: 10 - عملگر وروديبراي اين که بتوانيم هنگام اجراي برنامه مقاديري را وارد کنيم از عملگر ورودي >> استفاده ميکنيم. استفاده از دستور ورودي به شکل زير است:cin >> variable;variable نام يک متغير است.
اسلاید 35: مثال 10 – 1 استفاده از عملگر وروديبرنامۀ زير يک عدد از کاربر گرفته و همان عدد را دوباره در خروجي نمايش ميدهد:int main(){ // reads an integer from input: int m; cout << Enter a number: ; cin >> m; cout << your number is: << m << endl; return 0;}Enter a number: 52your number is: 52
اسلاید 36: عملگر ورودي نيز مانند عملگر خروجي به شکل جرياني رفتار ميکند. يعني همان طور که در عملگر خروجي ميتوانستيم چند عبارت را با استفاده از چند عملگر << به صورت پشت سر هم چاپ کنيم، در عملگر ورودي نيز ميتوانيم با استفاده از چند عملگر >> چند مقدار را به صورت پشت سر هم دريافت کنيم. مثلا با استفاده از دستور:cin >> x >> y >> z;سه مقدار x و y و z به ترتيب از ورودي دريافت ميشوند. براي اين کار بايد بين هر ورودي يک فضاي خالي (space) بگذاريد و پس از تايپ کردن همۀ وروديها، کليد enter را بفشاريد. آخرين مثال جلسه، اين موضوع را بهتر نشان ميدهد.
اسلاید 37: مثال 11 – 1 چند ورودي روي يک خطبرنامۀ زير مانند مثال 10 – 2 است با اين تفاوت که سه عدد را از ورودي گرفته و همان اعداد را دوباره در خروجي نمايش ميدهد:int main(){ // reads 3 integers from input: int q, r, s; cout << Enter three numbers: ; cin >> q >> r >> s; cout << your numbers are: << q << , << r << , << s << endl; return 0;}Enter three numbers: 35 70 9your numbers are: 35, 70, 9
اسلاید 38: پايان جلسه اول
اسلاید 39: جلسه دوم«انواع اصلي»
اسلاید 40: آنچه در اين جلسه مي خوانيد:1- انواع دادۀ عددي2- متغير عدد صحيح3- محاسبات اعداد صحيح4- عملگرهاي افزايشي و کاهشي5- عملگرهاي مقدارگذاري مرکب6- انواع مميز شناور›››
اسلاید 41: 7- تعريف متغير مميز شناور 8 - شکل علمي مقادير مميز شناور 9- نوع بولين bool 10- نوع کاراکتري char 11- نوع شمارشي enum 12- تبديل نوع، گسترش نوع›››
اسلاید 42: 13- برخي از خطاهاي برنامهنويسي 14 - سرريزي عددي 15- خطاي گرد کردن 16- حوزۀ متغيرها
اسلاید 43: هدف کلي: معرفي انواع متغييرها و نحوۀ بهکارگيري آنها در برنامههاي C++هدفهاي رفتاري: انتظار ميرود پس از پايان اين جلسه بتوانيد:- انواع عددي صحيح در C++ را نام ببريد و متغيرهايي از اين نوعها را در برنامهها به کار ببريد.- انواع عددي مميز شناور در C++ را نام ببريد و متغيرهايي از اين نوعها را در برنامهها به کار ببريد.- نوع بولين را تعريف کرده و متغيرهايي از اين نوع را در برنامهها به کار ببريد.>>>
اسلاید 44: - نوع شمارشي را شناخته و متغيرهايي از اين نوع را در برنامهها به کار ببريد.- مفاهيم «تبديل نوع» و «گسترش نوع» را شناخته و انواع مختلف را به يکديگر تبديل نماييد.- علت خطاهاي «سرريزي عددي» و «گردکردن» را دانسته و بتوانيد محل وقوع آنها را کشف کنيد.- عملگرهاي حسابي و افزايشي و کاهشي و مقدارگذاري مرکب را در برنامهها به کار ببريد.
اسلاید 45: ما در زندگي روزمره از دادههاي مختلفي استفاده ميکنيم: اعداد ، تصاوير، نوشتهها يا حروف الفبا، صداها، بوها و ... . با پردازش اين دادهها ميتوانيم تصميماتي اتخاذ کنيم، عکسالعملهايي نشان دهيم و مسالهاي را حل کنيم. رايانهها نيز قرار است همين کار را انجام دهند. يعني دادههايي را بگيرند، آنها را به شکلي که ما تعيين ميکنيم پردازش کنند و در نتيجه اطلاعات مورد نيازمان را استخراج کنند. مقدمه
اسلاید 46: 1- انواع دادۀ عدديدر C++ دو نوع اصلي داده وجود دارد: «نوع صحيح» و «نوع مميز شناور». همۀ انواع ديگر از روي اين دو ساخته ميشوند (به شکل زير دقت کنيد).
اسلاید 47: نوع صحيح نوع صحيح براي نگهداري اعداد صحيح (اعداد 0 و 1 و 2 و ...) استفاده ميشود. اين اعداد بيشتر براي شمارش به کار ميروند و دامنه محدودي دارند.
اسلاید 49: نوع مميز شناور براي نگهداري اعداد اعشاري استفاده ميشود. اعداد اعشاري بيشتر براي اندازهگيري دقيق به کار ميروند و دامنۀ بزرگتري دارند. يک عدد اعشاري مثل 352/187 را ميتوان به شکل 10×7352/18 يا 102×87352/1 يا1-10×52/1873يا2-10×2/18735 و يا ... نوشت. به اين ترتيب با کم و زياد کردن توان عدد 10 مميز عدد نيز جابهجا ميشود. به همين دليل است که به اعداد اعشاري «اعداد مميز شناور» ميگويند.
اسلاید 50: 2- متغير عدد صحيح C++ شش نوع متغير عدد صحيح دارد تفاوت اين شش نوع مربوط به ميزان حافظۀ مورد استفاده و محدودۀ مقاديري است که هر کدام ميتوانند داشته باشند. اين ميزان حافظۀ مورد استفاده و محدودۀ مقادير، بستگي زيادي به سختافزار و همچنين سيستم عامل دارد. يعني ممکن است روي يک رايانه، نوع int دو بايت از حافظه را اشغال کند در حالي که روي رايانهاي از نوع ديگر نوع int به چهار بايت حافظه نياز داشته باشد.
اسلاید 51: وقتي برنامهاي مينويسيد، توجه داشته باشيد که از نوع صحيح مناسب استفاده کنيد تا هم برنامه دچار خطا نشود و هم حافظۀ سيستم را هدر ندهيد.
اسلاید 52: 3 -محاسبات اعداد صحيح C++ مانند اغلب زبانهاي برنامهنويسي براي محاسبات از عملگرهاي جمع (+) ، تفريق (-) ، ضرب (*) ، تقسيم (/) و باقيمانده (%) استفاده ميکند.
اسلاید 53: 4 - عملگرهاي افزايشي و کاهشيC++ براي دستکاري مقدار متغيرهاي صحيح، دو عملگر جالب ديگر دارد:عملگر ++ :مقدار يک متغير را يک واحد افزايش ميدهد. عملگر -- : مقدار يک متغير را يک واحد کاهش ميدهد. اما هر کدام از اين عملگرها دو شکل متفاوت دارند: شکل «پيشوندي» و شکل «پسوندي».
اسلاید 54: در شکل پيشوندي، عملگر قبل از نام متغير ميآيد مثل ++m يا --n . در شکل پسوندي، عملگر بعد از نام متغير ميآيد مثل m++ يا n-- . در شکل پيشوندي ابتدا متغير، متناسب با عملگر، افزايش يا کاهش مييابد و پس از آن مقدار متغير براي محاسبات ديگر استفاده ميشود. در شکل پسوندي ابتدا مقدار متغير در محاسبات به کار ميرود و پس از آن مقدار متغير يک واحد افزايش يا کاهش مييابد.
اسلاید 55: 5 – عملگرهاي مقدارگذاري مرکبC++ عملگرهاي ديگري دارد که مقدارگذاري در متغيرها را تسهيل مينمايند. مثلا با استفاده از عملگر += ميتوانيم هشت واحد به m اضافه کنيم اما با دستور کوتاهتر:m += 8;دستور بالا معادل دستور m = m + 8; است با اين تفاوت که کوتاهتر است. به عملگر += «عملگر مرکب» ميگويند زيرا ترکيبي از عملگرهاي + و = ميباشد
اسلاید 56: 5- عملگرهاي مقدارگذاري مرکبقبلا از عملگر = براي مقدارگذاري در متغيرها استفاده کرديم. C++ عملگرهاي ديگري دارد که مقدارگذاري در متغيرها را تسهيل مينمايند. عملگر مرکب در C++ عبارتند از: += و -= و *= و /= و =%
اسلاید 57: نحوۀ عمل اين عملگرها به شکل زير است:m += 8; →m = m + 8;m -= 8; →m = m - 8;m *= 8; →m = m * 8;m /= 8; →m = m / 8;m %= 8; →m = m % 8;
اسلاید 58: 6 – انواع مميز شناورعدد مميز شناور به بيان ساده همان عدد اعشاري است. عددي مثل 123.45 يک عدد اعشاري است. براي اين که مقدار اين عدد در رايانه ذخيره شود، ابتدا بايد به شکل دودويي تبديل شود:123.45 = 1111011.01110012 اکنون براي مشخص نمودن محل اعشار در عدد، تمام رقمها را به سمت راست مميز منتقل ميکنيم. البته با هر جابجايي مميز، عدد حاصل بايد در تواني از 2 ضرب شود:123.45 = 0.11110110111001× 27 به مقدار 11110110111001 «مانتيس عدد» و به 7 که توان روي دو است، «نماي عدد» گفته ميشود.
اسلاید 59: درC++ سه نوع مميز شناور وجود دارد: نوع long double از هشت يا ده يا دوازده يا شانزده بايت براي نگهداري عدد استفاده ميکند. معمولا نوع float از چهار بايت براي نگهداري عدد استفاده ميکند. نوع double از هشت بايت براي نگهداري عدد استفاده ميکند.
اسلاید 60: جدول تخصيص حافظه براي متغيير هاي مميز شناور
اسلاید 61: 7 – تعريف متغير مميز شناورتعريف متغير مميز شناور مانند تعريف متغير صحيح است. با اين تفاوت که از کلمۀ کليدي float يا double براي مشخص نمودن نوع متغير استفاده ميکنيم. مثال:float x;double x,y=0;تفاوت نوع float با نوع double در اين است که نوع double دو برابر float از حافظه استفاده ميکند. پس نوع double دقتي بسيار بيشتر از float دارد. به همين دليل محاسبات double وقتگيرتر از محاسبات float است.
اسلاید 62: اعداد مميز شناور به دو صورت در ورودي و خروجي نشان داده ميشوند: به شکل «ساده» و به شکل «علمي».8- شکل علمي مقادير مميز شناور2- علمي 1.234567×104 1- ساده12345.67 مشخص است که شکل علمي براي نشان دادن اعداد خيلي کوچک و همچنين اعداد خيلي بزرگ، کارآيي بيشتري دارد.
اسلاید 63: نوع bool يک نوع صحيح است که متغيرهاي اين نوع فقط ميتوانند مقدار true يا false داشته باشند. true به معني درست و false به معني نادرست است. اما اين مقادير در اصل به صورت 1 و 0 درون رايانه ذخيره ميشوند: 1 براي true و 0 براي false. 9 – نوع بولين bool
اسلاید 64: 10- نوع کاراکتري char يک کاراکتر يک حرف، رقم يا نشانه است که يک شمارۀ منحصر به فرد دارد. به عبارت عاميانه، هر کليدي که روي صفحهکليد خود ميبينيد يک کاراکتر را نشان ميدهد. مثلا هر يک از حروف A تا Z و a تا z و هر يک از اعداد 0 تا 9 و يا نشانههاي ~ تا + روي صفحهکليد را يک کاراکتر مينامند.
اسلاید 65: براي تعريف متغيري از نوع کاراکتر از کلمه کليدي char استفاده ميکنيم. يک کاراکتر بايد درون دو علامت آپستروف () محصور شده باشد. پس A يک کاراکتر است؛ همچنين8 يک کاراکتر است اما 8 يک کاراکتر نيست بلکه يک عدد صحيح است . مثال:char c =A;
اسلاید 66: 11 – نوع شمارشي enum يک نوع شمارشي يک نوع صحيح است که توسط کاربر مشخص ميشود. نحو تعريف يک نوع شمارشي به شکل زير است:enum typename{enumerator-list} که enum کلمهاي کليدي است، typename نام نوع جديد است که کاربر مشخص ميکند و enumerator-list مجموعه مقاديري است که اين نوع جديد ميتواند داشته باشد.
اسلاید 67: به عنوان مثال به تعريف زير دقت کنيد:enum Day{SAT,SUN,MON,TUE,WED,THU,FRI} حالا Day يک نوع جديد است و متغيرهايي که از اين نوع تعريف ميشوند ميتوانند يکي از مقادير SAT و SUN و MON و TUE و WED و THU و FRI را داشته باشند:Day day1,day2;day1 = MON;day2 = THU; وقتي نوع جديد Day و محدودۀ مقاديرش را تعيين کرديم، ميتوانيم متغيرهايي از اين نوع جديد بسازيم. در کد بالا متغيرهاي day1 و day2 از نوع Day تعريف شدهاند. آنگاه day1 با مقدار MON و day2 با مقدار THU مقداردهي شده است.
اسلاید 68: مقادير SAT و SUN و ... هر چند که به همين شکل به کار ميروند اما در رايانه به شکل اعداد صحيح 0 و 1 و 2 و ... ذخيره ميشوند. به همين دليل است که به هر يک از مقادير SAT و SUN و ... يک شمارشگر ميگويند. ميتوان مقادير صحيح دلخواهي را به شمارشگرها نسبت داد:enum Day{SAT=1,SUN=2,MON=4,TUE=8,WED=16,THU=32,FRI=64} اگر فقط بعضي از شمارشگرها مقداردهي شوند، آنگاه ساير شمارشگرها که مقداردهي نشدهاند مقادير متوالي بعدي را خواهند گرفت:enum Day{SAT=1,SUN,MON,TUE,WED,THU,FRI} دستور بالا مقادير 1 تا 7 را به ترتيب به روزهاي هفته تخصيص خواهد داد. همچنين دو يا چند شمارشگر در يک فهرست ميتوانند مقادير يکساني داشته باشند:enum Answer{NO=0,FALSE=0,YES=1,TRUE=1,OK=1}
اسلاید 69: نام شمارشگر بايد معتبر باشد: يعني:1- کلمۀ کليدي نباشد.2- با عدد شروع نشود.3- نشانههاي رياضي نيز نداشته باشد. 1 – براي نام ثابتها از حروف بزرگ استفاده کنيد2 – اولين حرف از نام نوع شمارشي را با حرف بزرگ بنويسيد.3 – در هر جاي ديگر از حروف کوچک استفاده کنيد. نحوۀ انتخاب نامشمارشگرها آزاد است اما بيشتر برنامهنويسان از توافق زير در برنامههايشان استفاده ميکنند:
اسلاید 70: آخر اين که نام شمارشگرها نبايد به عنوان نام متغيرهاي ديگر در جاهاي ديگر برنامه استفاده شود. مثلا:enum Score{A,B,C,D}float B;char c; در تعريفهاي بالا B و C را نبايد به عنوان نام متغيرهاي ديگر به کار برد زيرا اين نامها در نوع شمارشي Score به کار رفته است . شمارشگرهاي همنام نبايد در محدودههاي مشترک استفاده شوند. براي مثال تعريفهاي زير را در نظر بگيريد:enum Score{A,B,C,D}enum Group{AB,B,BC}دو تعريف بالا غيرمجاز است زيرا شمارشگر B در هر دو تعريف Score و Group آمده است.
اسلاید 71: انواع شمارشي براي توليد کد «خود مستند» به کار ميروند، يعني کدي که به راحتي درک شود و نياز به توضيحات اضافي نداشته باشد. مثلا تعاريف زير خودمستند هستند زيرا به راحتي نام و نوع کاربرد و محدودۀ مقاديرشان درک ميشود:enum Color{RED,GREEN,BLUE,BLACK,ORANGE}enum Time{SECOND,MINUTE,HOUR}enum Date{DAY,MONTH,YEAR}enum Language{C,DELPHI,JAVA,PERL}enum Gender{MALE,FEMALE}
اسلاید 72: در محاسباتي که چند نوع متغير وجود دارد، جواب هميشه به شکل متغيري است که دقت بالاتري دارد. يعني اگر يک عدد صحيح را با يک عدد مميز شناور جمع ببنديم، پاسخ به شکل مميز شناور است به اين عمل گسترش نوع ميگويند. 12 – تبديل نوع، گسترش نوع براي اين که مقدار يک متغير از نوع مميز شناور را به نوع صحيح تبديل کنيم از عبارت int() استفاده ميکنيم به اين عمل تبديل نوع گفته مي شود
اسلاید 73: مثال تبديل نوع: اين برنامه، يک نوع double را به نوع int تبديل ميکند:int main(){ // casts a double value as an int: double v=1234.987; int n; n = int(v); cout << v = << v << , n = << n << endl; return 0;}مثالهاي زير تبديل نوع و گسترش نوع را نشان ميدهند. مثال گسترش نوع برنامۀ زير يک عدد صحيح را با يک عدد مميز شناور جمع ميکند:int main(){ // adds an int value with a double value: int n = 22; double p = 3.1415; p += n; cout << p = << p << , n = << n << endl; return 0;}
اسلاید 74: «خطاي زمان کامپايل» اين قبيل خطاها که اغلب خطاهاي نحوي هستند ، توسط کامپايلر کشف ميشوند و به راحتي ميتوان آنها را رفع نمود. «خطاي زمان اجرا» کشف اينگونه خطاها به راحتي ممکن نيست و کامپايلر نيز چيزي راجع به آن نميداند. برخي از خطاهاي زمان اجرا سبب ميشوند که برنامه به طور کامل متوقف شود و از کار بيفتد. 13 – برخي از خطاهاي برنامهنويسي
اسلاید 75: يک متغير هر قدر هم که گنجايش داشته باشد، بالاخره مقداري هست که از گنجايش آن متغير بيشتر باشد. اگر سعي کنيم در يک متغير مقداري قرار دهيم که از گنجايش آن متغير فراتر باشد، متغير «سرريز» ميشود،در چنين حالتي ميگوييم که خطاي سرريزي رخ داده است.14- سرريزي عددي
اسلاید 76: مثال 12 – 2 سرريزي عدد صحيحاين برنامه به طور مكرر n را در 1000 ضرب ميكند تا سرانجام سرريز شود:int main(){ //prints n until it overflows: int n =1000; cout << n = << n << endl; n *= 1000; // multiplies n by 1000 cout << n = << n << endl; n *= 1000; // multiplies n by 1000 cout << n = << n << endl; n *= 1000; // multiplies n by 1000 cout << n = << n << endl; return 0;} وقتي يک عدد صحيح سرريز شود، عدد سرريز شده به يک مقدار منفي «گردانيده» ميشود اما وقتي يک عدد مميز شناور سرريز شود، نماد inf به معناي بينهايت را به دست ميدهد.
اسلاید 77: 15 – خطاي گرد کردن خطاي گرد كردن نوع ديگري از خطاست كه اغلب وقتي رايانهها روي اعداد حقيقي محاسبه ميكنند، رخ ميدهد. براي مثال عدد 1/3ممكن است به صورت 0.333333 ذخيره شود كه دقيقا معادل 1/3 نيست. اين خطا از آنجا ناشي ميشود که اعدادي مثل 1/3 مقدار دقيق ندارند و رايانه نميتواند اين مقدار را پيدا کند، پس نزديکترين عدد قابل محاسبه را به جاي چنين اعدادي منظور ميکند. «هيچگاه از متغير مميز شناور براي مقايسه برابري استفاده نکنيد» زيرا در متغيرهاي مميز شناور خطاي گرد کردن سبب ميشود که پاسخ با آن چه مورد نظر شماست متفاوت باشد.
اسلاید 78: 16 – حوزۀ متغيرهاانتخاب نامهاي نامفهوم يا ناقص سبب کاهش خوانايي برنامه و افزايش خطاهاي برنامهنويسي ميشود. استفاده از متغيرها در حوزۀ نامناسب هم سبب بروز خطاهايي ميشود. «حوزه متغير» محدودهاي است که يک متغير خاص اجازه دارد در آن محدوده به کار رود يا فراخواني شود. اصطلاح «بلوک» در C++ واژه مناسبي است که ميتوان به وسيلۀ آن حوزۀ متغير را مشخص نمود. يک بلوک برنامه، قسمتي از برنامه است که درون يک جفت علامت کروشه { } محدود شده است.
اسلاید 79: حوزۀ يک متغير از محل اعلان آن شروع ميشود و تا پايان همان بلوک ادامه مييابد. خارج از آن بلوک نميتوان به متغير دسترسي داشت. همچنين قبل از اين که متغير اعلان شود نميتوان آن را استفاده نمود.ميتوانيم در يک برنامه، چند متغير متفاوت با يک نام داشته باشيم به شرطي که در حوزههاي مشترک نباشند.
اسلاید 80: پايان جلسه دوم
اسلاید 81: جلسه سوم«انتخاب»
اسلاید 82: آنچه در اين جلسه مي خوانيد:1- دستور if2- دستور if..else3- عملگرهاي مقايسهاي4- بلوكهاي دستورالعمل5- شرطهاي مركب6- ارزيابي ميانبري›››
اسلاید 83: 7- عبارات منطقي 8 - دستورهاي انتخاب تودرتو 9- ساختار else if 10- دستورالعمل switch 11- عملگر عبارت شرطي 12- كلمات كليدي
اسلاید 84: هدف کلي:شناخت انواع دستورالعملهاي انتخاب و شيوۀ بهکارگيري هر يک هدفهاي رفتاري:انتظار ميرود پس از پايان اين جلسه بتوانيد:- نحو دستور if را شناخته و آن را در برنامهها به کار ببريد.- نحو دستور if..else را شناخته و آن را در برنامهها به کار ببريد.- از ساختار else..if در تصميمگيريهاي پيچيده استفاده کنيد.- نحو دستور switch را شناخته و خطاي «تلۀ سقوط» را تشخيص دهيد.- بلوک دستورالعمل را تعريف کنيد.- عملگرهاي مقايسهاي و عملگر عبارت شرطي را در دستورات شرطي به کار ببريد.- از شرطهاي مرکب استفاده کرده و ارزيابي ميانبري را شرح دهيد.- «کلمۀ کليدي» را تعريف کنيد. >>>
اسلاید 85: همۀ برنامههايي که در دو جلسه اول بيان شد، به شکل ترتيبي اجرا ميشوند، يعني دستورات برنامه به ترتيب از بالا به پايين و هر کدام دقيقا يک بار اجرا ميشوند. در اين جلسه نشان داده ميشود چگونه از دستورالعملهاي انتخاب1 جهت انعطافپذيري بيشتر برنامه استفاده کنيم. همچنين در اين جلسه انواع صحيح كه در C++ وجود دارد بيشتر بررسي ميگردد.مقدمه
اسلاید 86: دستور if موجب ميشود برنامه به شکل شرطي اجرا شود. نحو آن به گونۀ زير است:If (condition) statement; Condition که شرط ناميده ميشود يك عبارت صحيح است (عبارتي که با يک مقدار صحيح برآورد ميشود) و statement ميتواند هر فرمان قابل اجرا باشد. Statement وقتي اجرا خواهد شد كه condition مقدار غير صفر داشته باشد. دقت كنيد كه شرط بايد درون پرانتز قرار داده شود.دستور if
اسلاید 87: 2- دستور if..elseدستور if..else موجب ميشود بسته به اين که شرط درست باشد يا خير، يكي از دو دستورالعمل فرعي اجرا گردد. نحو اين دستور به شکل زير است:if (condition) statement1;else statement2;condition همان شرط مساله است که يك عبارت صحيح ميباشد و statement1 و statement2 فرمانهاي قابل اجرا هستند. اگر مقدار شرط، غير صفر باشد، statement1 اجرا خواهد شد وگرنه statement2 اجرا ميشود.
اسلاید 88: int main(){ int n, d; cout << Enter two positive integers: ; cin >> n >> d; if (n%d) cout << n << is not divisible by << d << endl; else cout << n << is divisible by << d << endl;}مثال يک آزمون قابليت تقسيم
اسلاید 89: 4- عملگرهاي مقايسهايدر C++ شش عملگر مقايسهاي وجود دارد: < و > و <= و >= و == و != . هر يک از اين شش عملگر به شکل زير به کار ميروند:x < y // است y کوچکتر از x x > y // است y بزرگتر از xx <= y // است y کوچکتر يا مساوي xx >= y // است y بزرگتر يا مساوي xx == y // است y مساوي با xx != y // نيست y مساوي با x
اسلاید 90: اينها ميتوانند براي مقايسۀ مقدار عبارات با هر نوع ترتيبي استفاده شوند. عبارت حاصل به عنوان يك شرط تفسير ميشود. مقدار اين شرط صفر است اگر شرط نادرست باشد و غير صفر است اگر شرط درست باشد. براي نمونه، عبارت 7*8<6*5 برابر با صفر ارزيابي ميشود، به اين معني كه اين شرط نادرست است.
اسلاید 91: 2- متغير عدد صحيح C++ شش نوع متغير عدد صحيح دارد تفاوت اين شش نوع مربوط به ميزان حافظۀ مورد استفاده و محدودۀ مقاديري است که هر کدام ميتوانند داشته باشند. اين ميزان حافظۀ مورد استفاده و محدودۀ مقادير، بستگي زيادي به سختافزار و همچنين سيستم عامل دارد. يعني ممکن است روي يک رايانه، نوع int دو بايت از حافظه را اشغال کند در حالي که روي رايانهاي از نوع ديگر نوع int به چهار بايت حافظه نياز داشته باشد.
اسلاید 92: مثلا دستور x = 33; مقدار 33 را در x قرار ميدهد ولي دستور x == 33; بررسي ميکند که آيا مقدار x با 33 برابر است يا خير. درک اين تفاوت اهميت زيادي دارد.دقت کنيد كه در ++C عملگر جايگزيني با عملگر برابري فرق دارد. عملگر جايگزيني يک مساوي تکي = است ولي عملگر برابري، دو مساوي = = است.
اسلاید 93: 4- بلوكهاي دستورالعمليك بلوك دستورالعمل زنجيرهاي از دستورالعملهاست كه درون براكت {} محصور شده، مانند :{ int temp=x; x = y; y = temp;}در برنامههاي ++C يک بلوک دستورالعمل مانند يک دستورالعمل تکي است.
اسلاید 94: int main(){ int x, y; cout << Enter two integers: ; cin >> x >> y; if (x > y) { int temp = x; x = y; y = temp; } //swap x and y cout << x << <= << y << endl;}مثال : يك بلوك دستورالعمل درون يك دستور ifاين برنامه دو عدد صحيح را گرفته و به ترتيب بزرگتري، آنها را چاپ ميكند:
اسلاید 95: int main(){ int n=44; cout << n = << n << endl; { int n; cout << Enter an integer: ; cin >> n; cout << n = << n << endl; } { cout << n = << n << endl; } { int n; cout << n = << n << endl; } cout << n = << n << endl; }
اسلاید 96: 5 – شرطهاي مركبشرطهايي مانند n%d و x>=y ميتوانند به صورت يك شرط مركب با هم تركيب شوند. اين كار با استفاده ازعملگرهاي منطقي && (and) و || (or) و ! (not) صورت ميپذيرد. اين عملگرها به شکل زير تعريف ميشوند:p && q درست است اگر و تنها اگر هم p و هم q هر دو درست باشندp || q نادرست است اگر و تنها اگر هم p و هم q هر دو نادرست باشند!pدرست است اگر و تنها اگر p نادرست باشد براي مثال(n%d || x>=y) نادرست است اگر و تنها اگر n%d برابر صفر و x كوچكتر از y باشد.
اسلاید 97: سه عملگر منطقي && (and) و || (or) و ! (not) معمولا با استفاده از جداول درستي به گونۀ زير بيان ميشوند:طبق جدولهاي فوق اگر p درست و q نادرست باشد، عبارت p&&q نادرست و عبارت p||q درست است.
اسلاید 98: 6- ارزيابي ميانبريعملگرهاي && و || به دو عملوند نياز دارندتا مقايسه را روي آن دو انجام دهند. جداول درستي نشان ميدهد که p&&q نادرست است اگر p نادرست باشد. در اين حالت ديگر نيازي نيست که q بررسي شود. همچنين p||q درست است اگر p درست باشد و در اين حالت هم نيازي نيست که q بررسي شود. در هر دو حالت گفته شده، با ارزيابي عملوند اول به سرعت نتيجه معلوم ميشود. اين كار ارزيابي ميانبري ناميده ميشود. شرطهاي مركب كه از && و || استفاده ميكنند عملوند دوم را بررسي نميكنند مگر اين كه لازم باشد.
اسلاید 99: 7- عبارات منطقييك عبارت منطقي شرطي است كه يا درست است يا نادرست. قبلا ديديم که عبارات منطقي با مقادير صحيح ارزيابي ميشوند. مقدار صفر به معناي نادرست و هر مقدار غير صفر به معناي درست است. به عبارات منطقي «عبارات بولي» هم ميگويند.
اسلاید 100: چون همۀ مقادير صحيح ناصفر به معناي درست تفسير ميشوند، عبارات منطقي اغلب تغيير قيافه ميدهند. براي مثال دستورif (n) cout << n is not zero; وقتي n غير صفر است عبارت n is not zero را چاپ ميكند زيرا عبارت منطقي (n) وقتي مقدار n غير صفر است به عنوان درست تفسير ميگردد.
اسلاید 101: کد زير را نگاه کنيد:if (n%d) cout << n is not a multiple of d;دستور خروجي فقط وقتي كه n%d ناصفر است اجرا ميگردد و n%d وقتي ناصفر است که n بر d بخشپذير نباشد. گاهي ممکن است فراموش کنيم که عبارات منطقي مقادير صحيح دارند و اين فراموشي باعث ايجاد نتايج غير منتظره و نامتعارف شود.
اسلاید 102: يك خطاي منطقي ديگر، اين برنامه خطادار است:int main(){ int n1, n2, n3; cout << Enter three integers: ; cin >> n1 >> n2 >> n3; if (n1 >= n2 >= n3) cout << max = << n1; }منشأ خطا در برنامۀ بالا اين اصل است كه عبارات منطقي مقدارهاي عددي دارند.
اسلاید 103: دستورهاي انتخاب ميتوانند مانند دستورالعملهاي مركب به كار روند. به اين صورت که يك دستور انتخاب ميتواند درون دستور انتخاب ديگر استفاده شود. به اين روش، جملات تودرتو ميگويند. 8- دستورهاي انتخاب تودرتو
اسلاید 104: مثال 12-3 دستورهاي انتخاب تودرتواين برنامه همان اثر مثال 10-3 را دارد:int main(){ int n, d; cout << Enter two positive integers: ; cin >> n >> d; if (d != 0) if (n%d = = 0) cout << d << divides << n << endl; else cout << d << does not divide << n << endl; else cout << d << does not divide << n << endl;} در برنامۀ بالا، دستور if..else دوم درون دستور if..else اول قرار گرفته است. وقتي دستور if..else به شکل تو در تو به کار ميرود، كامپايلر از قانون زير جهت تجزيه اين دستورالعمل مركب استفاده ميكند:« هر else با آخرين if تنها جفت ميشود.»
اسلاید 105: 9- ساختار else if دستور if..else تودرتو، اغلب براي بررسي مجموعهاي از حالتهاي متناوب يا موازي به كار ميرود. در اين حالات فقط عبارت else شامل دستور if بعدي خواهد بود. اين قبيل کدها را معمولا با ساختار else ifميسازند.
اسلاید 106: استفاده از ساختار else if براي مشخص کردن محدودۀ نمره برنامۀ زير يك نمرۀ امتحان را به درجۀ حرفي معادل تبديل ميكند:int main(){ int score; cout << Enter your test score: ; cin >> score; if (score > 100) cout << Error: that score is out of range.; else if (score >= 90) cout << Your grade is an A. << endl; else if (score >= 80) cout << Your grade is a B. << endl; else if (score >= 70) cout << Your grade is a C. << endl; else if (score >= 60) cout << Your grade is a D. << endl; else if (score >= 0) cout << Your grade is an F. << endl; else cout << Error: that score is out of range.;}
اسلاید 107: 10- دستورالعمل switch دستور switch ميتواند به جاي ساختار else if براي بررسي مجموعهاي از حالتهاي متناوب و موازي به كار رود. نحو دستور switch به شکل زير است:switch (expression){ case constant1: statementlist1; case constant2: statementlist2; case constant3: statementlist3; : : case constantN: statementlistN; default: statementlist0;}
اسلاید 108: اين دستور ابتدا expression را برآورد ميكند و سپس ميان ثابتهاي case به دنبال مقدار آن ميگردد. اگر مقدار مربوطه از ميان ثابتهاي فهرستشده يافت شد، دستور statementlist مقابل آن case اجرا ميشود. اگر مقدار مورد نظر ميان caseها يافت نشد و عبارت default وجود داشت، دستور statementlist مقابل آن اجرا ميشود. عبارتdefault يک عبارت اختياري است. يعني ميتوانيم در دستور switch آن را قيد نکنيم. expression بايد به شکل يك نوع صحيح ارزيابي شود و constantها بايد ثابتهاي صحيح باشند.
اسلاید 109: لازم است در انتهاي هر case دستور break قرار بگيرد. بدون اين دستور، اجراي برنامه پس از اين كه case مربوطه را اجرا کرد از دستور switch خارج نميشود، بلکه همۀ caseهاي زيرين را هم خط به خط ميپيمايد و دستورات مقابل آنها را اجرا ميکند. به اين اتفاق، تلۀ سقوط ميگويند.case constant1: statementlist1;break;
اسلاید 110: عملگر عبارت شرطي يکي از امکاناتي است که جهت اختصار در کدنويسي تدارک ديده شده است. اين عملگر را ميتوانيم به جاي دستور if..else به کار ببريم. اين عملگر از نشانههاي ? و : به شکل زير استفاده ميكند:condition ? expression1 : expression2; 11- عملگر عبارت شرطيدر اين عملگر ابتدا شرط condition بررسي ميشود. اگر اين شرط درست بود، حاصل کل عبارت برابر با expression1 ميشود و اگر شرط نادرست بود، حاصل کل عبارت برابر با expression2 ميشود.
اسلاید 111: مثلا در دستور انتساب زير:min = ( x<y ? x : y ); اگر x<y باشد مقدار x را درون min قرار ميدهد و اگر x<y نباشد مقدار y را درون min قرار ميدهد. يعني به همين سادگي و اختصار، مقدار کمينۀ x و y درون متغير min قرار ميگيرد.
اسلاید 112: اکنون با کلماتي مثل if و case و float آشنا شديم. دانستيم که اين کلمات براي C++ معاني خاصي دارند. از اين کلمات نميتوان به عنوان نام يک متغير يا هر منظور ديگري استفاده کرد و فقط بايد براي انجام همان کار خاص استفاده شوند. مثلا کلمۀ float فقط بايد براي معرفي يک نوع اعشاري به کار رود. 12- كلمات كليدييك كلمۀ كليدي در يك زبان برنامهنويسي كلمهاي است كه از قبل تعريف شده و براي هدف مشخصي منظور شده است.
اسلاید 113: C++ استاندارد اكنون شامل 74 كلمۀ كليدي است:
اسلاید 116: يك كلمۀ رزرو شده كلمهاي است که يک دستور خاص از آن زبان را نشان ميدهد. كلمۀ كليدي if و else كلمات رزرو شده هستند.دو نوع كلمۀ كليدي وجود دارد:1- كلمههاي رزرو شده 2- شناسههاي استاندارد. يك شناسۀ استاندارد كلمهاي است كه يك نوع دادۀ استاندارد از زبان را مشخص ميكند. كلمات كليدي bool و int شناسههاي استاندارد هستند
اسلاید 117: پايان جلسه سوم
اسلاید 118: جلسه چهارم«تكرار»
اسلاید 119: آنچه در اين جلسه مي خوانيد:1- دستور while2- خاتمه دادن به يك حلقه3- دستور do..while4- دستور for5- دستور break6- دستور continue7- دستور goto8- توليد اعداد شبه تصادفي
اسلاید 120: هدفهاي رفتاري:انتظار ميرود پس از مطالعۀ اين جلسه بتوانيد:- نحو دستورwhile را شناخته و از آن براي ايجاد حلقه استفاده کنيد.- نحو دستور do..while را شناخته و تفاوت آن با دستور while را بيان کنيد.- نحو دستور for را شناخته و با استفاده از آن حلقههاي گوناگون بسازيد.- حلقههاي فوق را به يکديگر تبديل کنيد.- علت استفاده از «دستورات پرش» را ذکر کرده و تفاوت سه دستور break و continue و goto را بيان کنيد.- اهميت اعداد تصادفي را بيان کرده و نحوۀ توليد «اعداد شبه تصادفي» را بدانيد.هدف کلي:شناخت انواع ساختارهاي تکرار و نحو آنها و تبديل آنها به يکديگر.
اسلاید 121: مقدمه تكرار، اجراي پي در پي يك دستور يا بلوكي از دستورالعملها در يك برنامه است. با استفاده از تکرار ميتوانيم کنترل برنامه را مجبور کنيم تا به خطوط قبلي برگردد و آنها را دوباره اجرا نمايد.C++ داراي سه دستور تكرار است: دستور while، دستور do_while و دستور for. دستورهاي تکرار به علت طبيعت چرخهمانندشان، حلقه نيز ناميده ميشوند.
اسلاید 122: 1- دستور while نحو دستور while به شکل زير است:while (condition) statement;به جاي condition، يك شرط قرار ميگيرد و به جاي statement دستوري که بايد تکرار شود قرار ميگيرد. اگر مقدار شرط، صفر(يعني نادرست) باشد، statement ناديده گرفته ميشود و برنامه به اولين دستور بعد از while پرش ميكند. اگر مقدار شرط ناصفر(يعني درست) باشد، statement اجرا شده و دوباره مقدار شرط بررسي ميشود. اين تکرار آن قدر ادامه مييابد تا اين که مقدار شرط صفر شود.
اسلاید 123: مثال 1-4 محاسبۀ حاصل جمع اعداد صحيح متوالي با حلقۀ whileاين برنامه مقدار 1 + 2 + 3 + … + n را براي عدد ورودي n محاسبه ميكند:int main(){ int n, i=1; cout << Enter a positive integer: ; cin >> n; long sum=0; while (i <= n) sum += i++; cout << The sum of the first << n << integers is << sum;}
اسلاید 124: int main(){ int n, i=1; cout << Enter a positive integer: ; cin >> n; long sum=0; while (true) { if (i > n) break; sum += i++; } cout << The sum of the first << n << integers is << sum;} 2- خاتمه دادن به يك حلقهقبلا ديديم كه چگونه دستور break براي كنترل دستورالعمل switch استفاده ميشود (به مثال 17-4 نگاه كنيد). از دستور break براي پايان دادن به حلقهها نيز ميتوان استفاده کرد. يكي از مزيتهاي دستور break اين است كه فورا حلقه را خاتمه ميدهد بدون اين که مابقي دستورهاي درون حلقه اجرا شوند.
اسلاید 125: * مثال 4-4 اعداد فيبوناچياعداد فيبوناچي F0, F1, F2, F3, … به شکل بازگشتي توسط معادلههاي زير تعريف ميشوند:F0 = 0 , F1 = 1 , Fn = Fn-1 + Fn-2مثلا براي n=2 داريم:F2 = F2-1 + F2-2 = F1 + F0 = 0 + 1 = 1يا براي n=3 داريم:F3 = F3-1 + F3-2 = F2 + F1 = 1 + 1 = 2و براي n=4 داريم:F4 = F4-1 + F4-2 = F3 + F2 = 2 + 1 = 3
اسلاید 126: برنامۀ زير، همۀ اعداد فيبوناچي را تا يك محدودۀ مشخص که از ورودي دريافت ميشود، محاسبه و چاپ ميكند:int main(){ long bound; cout << Enter a positive integer: ; cin >> bound; cout << Fibonacci numbers < << bound << :n0, 1; long f0=0, f1=1; while (true) { long f2 = f0 + f1; if (f2 > bound) break; cout << , << f2; f0 = f1; f1 = f2;}}Enter a positive integer: 1000Fibonacci numbers < 1000:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987
اسلاید 127: int main(){ long bound; cout << Enter a positive integer: ; cin >> bound; cout << Fibonacci numbers < << bound << :n0, 1; long f0=0, f1=1; while (true) { long f2 = f0 + f1; if (f2 > bound) exit(0); cout << , << f2; f0 = f1; f1 = f2; }} برنامهنويسان ترجيح ميدهند از break براي خاتمه دادن به حلقههاي نامتناهي استفاده کنند زيرا قابليت انعطاف بيشتري دارد. مثال5-4 استفاده از تابع exit(0)تابع exit(0) روش ديگري براي خاتمه دادن به يك حلقه است. هرچند که اين تابع بلافاصله اجراي کل برنامه را پايان ميدهد:
اسلاید 128: متوقف کردن يك حلقۀ نامتناهي : با فشردن کليدهاي Ctrl+C سيستم عامل يک برنامه را به اجبار خاتمه ميدهد. كليد Ctrl را پايين نگه داشته و كليد C روي صفحهكليد خود را فشار دهيد تا برنامۀ فعلي خاتمه پيدا کند.
اسلاید 129: 3- دستور do..whileساختار do..while روش ديگري براي ساختن حلقه است. نحو آن به صورت زير است:do statement while (condition);به جاي condition يك شرط قرار ميگيرد و به جاي statement دستور يا بلوکي قرار ميگيرد که قرار است تکرار شود. اين دستور ابتدا statement را اجرا ميكند و سپس شرط condition را بررسي ميكند. اگر شرط درست بود حلقه دوباره تکرار ميشود وگرنه حلقه پايان مييابد.
اسلاید 130: دستور do..while مانند دستور while است. با اين فرق كه شرط کنترل حلقه به جاي اين که در ابتداي حلقه ارزيابي گردد، در انتهاي حلقه ارزيابي ميشود. يعني هر متغير كنترلي به جاي اين كه قبل از شروع حلقه تنظيم شود، ميتواند درون آن تنظيم گردد.نتيجۀ ديگر اين است كه حلقۀ do..while هميشه بدون توجه به مقدار شرط كنترل، لااقل يك بار اجرا ميشود اما حلقۀ while ميتواند اصلا اجرا نشود.
اسلاید 131: مثال 7-4 محاسبۀ حاصل جمع اعداد صحيح متوالي با حلقۀ do..whileاين برنامه همان تأثير مثال 1-5 را دارد:int main(){ int n, i=0; cout << Enter a positive integer: ; cin >> n; long sum=0; do sum += i++; while (i <= n); cout << The sum of the first << n << integers is << sum;}
اسلاید 132: * مثال 8-4 اعداد فاكتوريالاعداد فاكتوريال 0! و 1! و 2! و 3! و … با استفاده از رابطههاي بازگشتي زير تعريف ميشوند:0! = 1 , n! = n(n-1)!براي مثال، به ازاي n = 1 در معادلۀ دوم داريم:1! = 1((1-1)!) = 1(0!) = 1(1) = 1همچنين براي n = 2 داريم:2! = 2((2-1)!) = 2(1!) = 2(1) = 2و به ازاي n = 3 داريم:3! = 3((3-1)!) = 3(2!) = 3(2) = 6
اسلاید 133: برنامۀ زير همۀ اعداد فاكتوريال را که از عدد داده شده کوچکترند، چاپ ميکند:int main(){ long bound; cout << Enter a positive integer: ; cin >> bound; cout << Factorial numbers < << bound << :n1; long f=1, i=1; do { cout << , << f; f *= ++i; } while (f < bound);}
اسلاید 134: نحو دستورالعمل for به صورت زير است:for (initialization; condition; update) statement;سه قسمت داخل پرانتز، حلقه را کنترل ميکنند. 4 - دستور forعبارت initialization براي اعلان يا مقداردهي اوليه به متغير کنترل حلقه استفاده ميشود.اين عبارت اولين عبارتي است که ارزيابي ميشود پيش از اين که نوبت به تکرارها برسد. عبارت condition براي تعيين اين که آيا حلقه بايد تکرار شود يا خير به کار ميرود. يعني اين عبارت، شرط کنترل حلقه است. اگر اين شرط درست باشد دستور statement اجرا ميشود. عبارت updateبراي پيشبردن متغير کنترل حلقه به کار ميرود. اين عبارت پس از اجراي statement ارزيابي ميگردد.
اسلاید 135: بنابراين زنجيرۀ وقايعي که تکرار را ايجاد ميکنند عبارتند از:1 – ارزيابي عبارت initialization2 – بررسي شرط condition . اگر نادرست باشد، حلقه خاتمه مييابد.3 – اجراي statement4 – ارزيابي عبارت update5 – تکرار گامهاي 2 تا 4عبارتهاي initialization و condition و updateعبارتهاي اختياري هستند. يعني ميتوانيم آنها را در حلقه ذکر نکنيم.
اسلاید 136: مثال 9-4 استفاده از حلقۀ for براي محاسبۀ مجموع اعداد صحيح متوالياين برنامه همان تأثير مثال 1-5 را دارد:int main(){ int n; cout << Enter a positive integer: ; cin >> n; long sum=0; for (int i=1; i <= n; i++) sum += I; cout << The sum of the first << n << integers is << sum;}در C++ استاندارد وقتي يك متغير كنترل درون يك حلقۀ for اعلان ميشود (مانند i در مثال بالا) حوزۀ آن متغير به همان حلقۀ for محدود ميگردد. يعني آن متغير نميتواند بيرون از آن حلقه استفاده شود. نتيجۀ ديگر اين است که ميتوان از نام مشابهي در خارج از حلقۀ for براي يك متغير ديگر استفاده نمود.
اسلاید 137: مثال 12-4 يك حلقۀ for نزوليبرنامۀ زير ده عدد صحيح مثبت را به ترتيب نزولي چاپ ميكند:int main(){ for (int i=10; i > 0; i--) cout << << i;}
اسلاید 138: مثال 15-4 بيشتر از يك متغير كنترل در حلقۀ forحلقۀ for در برنامۀ زير دو متغير كنترل دارد:int main(){ for (int m=95, n=11, m%n > 0; m -= 3, n++) cout << m << % << n << = << m%n << endl;}
اسلاید 139: مثال 16-4 حلقههاي for تودرتوبرنامۀ زير يك جدول ضرب چاپ ميكند:#include <iomanip> #include <iostream> int main(){ for (int x=1; x <= 10; x++) { for (int y=1; y <= 10; y++) cout << setw(4) << x*y; cout << endl; }}
اسلاید 140: دستور break يک دستور آشناست. قبلا از آن براي خاتمه دادن به دستور switch و همچنين حلقههاي while و do..while استفاده کردهايم. از اين دستور براي خاتمه دادن به حلقۀ for نيز ميتوانيم استفاده کنيم. دستور break در هر جايي درون حلقه ميتواند جا بگيرد و در همان جا حلقه را خاتمه دهد. 5- دستور breakوقتي دستور break درون حلقههاي تودرتو استفاده شود، فقط روي حلقهاي که مستقيما درون آن قرار گرفته تاثير ميگذارد. حلقههاي بيروني بدون هيچ تغييري ادامه مييابند.
اسلاید 141: 6- دستور continueدستور break بقيۀ دستورهاي درون بلوك حلقه را ناديده گرفته و به اولين دستور بيرون حلقه پرش ميكند. دستور continue نيز شبيه همين است اما به جاي اين که حلقه را خاتمه دهد، اجرا را به تكرار بعدي حلقه منتقل ميكند. اين دستور، ادامۀ چرخۀ فعلي را لغو کرده و اجراي دور بعدي حلقه را آغاز ميکند.
اسلاید 142: مثال 19-4 استفاده از دستورهاي break و continueاين برنامۀ كوچك، دستورهاي break و continue را شرح ميدهد:int main(){ int n = 1; char c; for( ; ;n++ ) { cout << nLoop no: << n << endl; cout << Continue? <y|n> ; cin >> c; if (c = = y) continue; break; } cout << nTotal of loops: << n;}
اسلاید 143: دستورgoto نوع ديگري از دستورهاي پرش است. مقصد اين پرش توسط يك برچسب معين ميشود. برچسب شناسهاي است كه جلوي آن علامت كولن( : ) ميآيد و جلوي يك دستور ديگر قرار ميگيرد. يک مزيت دستور goto اين است که با استفاده از آن ميتوان از همۀ حلقههاي تودرتو خارج شد و به مکان دلخواهي در برنامه پرش نمود. 7- دستور goto
اسلاید 144: مثال 20-4 استفاده از دستور goto براي خارج شدن از حلقههاي تودرتوint main(){ const int N=5; for (int i=0; i<N; i++) { for (int j=0; j<N; j++) { for (int k=0; k<N; k++) if (i+j+k>N) goto esc; else cout << i+j+k << ; cout << * ; } esc: cout << . << endl; }}
اسلاید 145: يكي از كاربردهاي بسيار مهم رايانهها، «شبيهسازي» سيستمهاي دنياي واقعي است. تحقيقات و توسعههاي بسيار پيشرفته به اين راهکار خيلي وابسته است. به وسيلۀ شبيهسازي ميتوانيم رفتار سيستمهاي مختلف را مطالعه کنيم بدون اين که لازم باشد واقعا آنها را پيادهسازي نماييم. در شبيهسازي نياز است «اعداد تصادفي» توسط رايانهها توليد شود تا نادانستههاي دنياي واقعي مدلسازي شود. 8- توليد اعداد شبه تصادفي
اسلاید 146: رايانهها «ثابتکار» هستند يعني با دادن دادههاي مشابه به رايانههاي مشابه، هميشه خروجي يکسان توليد ميشود. با وجود اين ميتوان اعدادي توليد کرد که به ظاهر تصادفي هستند؛ اعدادي که به طور يکنواخت در يک محدودۀ خاص گستردهاند و براي هيچکدام الگوي مشخصي وجود ندارد. چنين اعدادي را «اعداد شبهتصادفي» ميناميم.
اسلاید 147: مثال 22-4 توليد اعداد شبه تصادفياين برنامه از تابع rand() براي توليد اعداد شبهتصادفي استفاده ميكند:#include<cstdlib>//defines the rand() and RAND_MAX#include <iostream>int main(){ // prints pseudo-random numbers: for (int i = 0; i < 8; i++) cout << rand() << endl; cout << RAND_MAX = << RAND_MAX << endl;}هر بار که برنامۀ بالا اجرا شود، رايانه هشت عدد صحيح unsigned توليد ميکند که به طور يکنواخت در فاصلۀ 0 تا RAND_MAX گسترده شدهاند. RAND_MAX در اين رايانه برابر با 2,147,483,647 است.
اسلاید 148: هر عدد شبهتصادفي از روي عدد قبلي خود ساخته ميشود. اولين عدد شبهتصادفي از روي يك مقدار داخلي که «هسته» گفته ميشود ايجاد ميگردد. هر دفعه که برنامه اجرا شود، هسته با يک مقدار پيشفرض بارگذاري ميشود. براي حذف اين اثر نامطلوب که از تصادفي بودن اعداد ميکاهد، ميتوانيم با استفاده از تابع ()srand خودمان مقدار هسته را انتخاب کنيم.
اسلاید 149: #include <cstdlib> // defines the rand() and srand()#include <iostream>int main(){ // prints pseudo-random numbers: unsigned seed; cout << Enter seed: ; cin >> seed; srand(seed); // initializes the seed for (int i = 0; i < 8; i++) cout << rand() << endl;}مثال 23-4 كارگذاري هسته به طور محاورهاياين برنامه مانند برنامۀ مثال 22-4 است بجز اين كه ميتوان هستۀ توليدکنندۀ اعداد تصادفي را به شکل محاورهاي وارد نمود:
اسلاید 150: پايان جلسه چهارم
اسلاید 151: جلسه پنجم« توابع»
اسلاید 152: آنچه در اين جلسه مي خوانيد:1- توابع كتابخانهاي C++ استاندارد2- توابع ساخت كاربر3- برنامۀ آزمون4- اعلانها و تعاريف تابع5- كامپايل جداگانۀ توابع6- متغيرهاي محلي، توابع محلي›››
اسلاید 153: 7- تابع void 8 - توابع بولي 9- توابع ورودي/خروجي (I/O) 10- ارسال به طريق ارجاع (آدرس) 11- ارسال از طريق ارجاع ثابت 12-توابع بيواسطه ›››
اسلاید 154: 13- چندشکلي توابع 14- تابع main() 15- آرگومانهاي پيشفرض
اسلاید 155: هدف کلي:شناخت و معرفي توابع و مزاياي استفاده از تابع در برنامههاهدفهاي رفتاري:انتظار ميرود پس از پايان اين جلسه بتوانيد:- اهميت توابع و مزيت استفاده از آنها را بيان کنيد.- «اعلان» و «تعريف» تابع را بدانيد و خودتان توابعي را ايجاد کنيد.- «برنامۀ آزمون» را تعريف کرده و دليل استفاده از آن را بيان نماييد.- مفهوم «آرگومان» را بدانيد.- تفاوت ارسال به طريق «ارجاع» و ارسال به طريق «مقدار» و ارسال به طريق «ارجاع ثابت» را بيان کنيد و شکل استفاده از هر يک را بدانيد.›››
اسلاید 156: - «تابع بيواسطه» را شناخته و نحوۀ معرفي آن را بدانيد.- چندشکلي توابع را تعريف کنيد و شيوۀ آن را بدانيد.- طريقۀ بهکارگيري آرگومانهاي پيشفرض را بدانيد.- فرق بين تابع void با ساير توابع را بدانيد.
اسلاید 157: 1-مقدمهبرنامههاي واقعي و تجاري بسيار بزرگتر از برنامههايي هستند که تاکنون بررسي کرديم. براي اين که برنامههاي بزرگ قابل مديريت باشند، برنامهنويسان اين برنامهها را به زيربرنامههايي بخشبندي ميکنند. اين زيربرنامهها «تابع» ناميده ميشوند. توابع را ميتوان به طور جداگانه کامپايل و آزمايش نمود و در برنامههاي مختلف دوباره از آنها استفاده کرد.
اسلاید 158: 2- توابع كتابخانهاي C++ استاندارد«كتابخانۀ C++ استاندارد» مجموعهاي است که شامل توابع از پيش تعريف شده و ساير عناصر برنامه است. اين توابع و عناصر از طريق «سرفايلها» قابل دستيابياند.قبلا برخي از آنها را استفاده كردهايم: ثابت INT_MAX که در <climits> تعريف شده ، تابع ()sqrt که در <cmath> تعريف شده است و... .
اسلاید 159: تابع جذر sqrt()ريشۀ دوم يك عدد مثبت، جذر آن عدد است.تابع مانند يک برنامۀ کامل، داراي روند ورودي - پردازش - خروجي است هرچند که پردازش، مرحلهاي پنهان است. يعني نميدانيم که تابع روي عدد 2 چه اعمالي انجام ميدهد که 41421/1 حاصل ميشود.
اسلاید 160: برنامۀ سادۀ زير، تابع از پيش تعريف شدۀ جذر را به کار ميگيرد:#include <cmath> // defines the sqrt() function#include <iostream> // defines the cout object using namespace std;int main(){ //tests the sqrt() function: for (int x=0; x < 6; x++) cout << t << x << t << sqrt(x) << endl;} براي اجراي يك تابع مانند تابع sqrt() کافي است نام آن تابع به صورت يک متغير در دستورالعمل مورد نظر استفاده شود، مانند زير:y=sqrt(x);
اسلاید 161: اين کار «فراخواني تابع» يا «احضار تابع» گفته ميشود. بنابراين وقتي كد sqrt(x) اجرا شود، تابع sqrt() فراخواني ميگردد. عبارت x درون پرانتز «آرگومان» يا «پارامتر واقعي» فراخواني ناميده ميشود. در چنين حالتي ميگوييم كه x توسط «مقدار» به تابع فرستاده ميشود. لذا وقتي x=3 است، با اجراي کد sqrt(x) تابع sqrt() فراخواني شده و مقدار 3 به آن فرستاده ميشود. تابع مذکور نيز حاصل 1.73205 را به عنوان پاسخ برميگرداند…
اسلاید 162: … اين فرايند در نمودار زير نشان داده شده. 31.73205xyMain()doubleintSqrt()31.73205متغيرهاي x و y در تابع main() تعريف شدهاند. مقدار x که برابر با 3 است به تابع sqrt() فرستاده ميشود و اين تابع مقدار 1.73205 را به تابع main() برميگرداند. جعبهاي كه تابع sqrt() را نشان ميدهد به رنگ تيره است، به اين معنا كه فرايند داخلي و نحوۀ کار آن قابل رويت نيست.
اسلاید 163: مثال 2-5 آزمايش يك رابطۀ مثلثاتياين برنامه هم از سرفايل <cmath> استفاده ميكند. هدف اين است که صحت رابطۀ Sin2x=2SinxCosx به شکل تجربي بررسي شود.int main(){ for (float x=0; x < 2; x += 0.2) cout << x << tt << sin(2*x) << t“ << 2*sin(x)*cos(x) << endl;}
اسلاید 164: 0 0 00.2 0.389418 0.3894180.4 0.717356 0.7173560.6 0.932039 0.9320390.8 0.999574 0.9995741 0.909297 0.9092971.2 0.675463 0.6754631.4 0.334988 0.3349881.6 -0.0583744 -0.05837441.8 -0.442521 -0.442521برنامۀ مقدار x را در ستون اول، مقدار Sin2x را در ستون دوم و مقدار 2SinxCosx را در ستون سوم چاپ ميكند. خروجي برنامه:خروجي نشان ميدهد که براي هر مقدار آزمايشي x، مقدار Sin2x با مقدار 2SinxCosx برابر است.
اسلاید 165: بيشتر توابع معروف رياضي كه در ماشينحسابها هم وجود دارد در سرفايل <cmath> تعريف شده است. بعضي از اين توابع در جدول زير نشان داده شده:
اسلاید 167: توجه داشته باشيد که هر تابع رياضي يک مقدار از نوع double را برميگرداند. اگر يك نوع صحيح به تابع فرستاده شود، قبل از اين كه تابع آن را پردازش کند، مقدارش را به نوع double ارتقا ميدهد.
اسلاید 168: بعضي از سرفايلهاي كتابخانۀ C++ استاندارد که کاربرد بيشتري دارند در جدول زير آمده است:اين سرفايلها از كتابخانۀ C استاندارد گرفته شدهاند. استفاده از آنها شبيه استفاده از سرفايلهاي C++ استاندارد (مانند <iostream> ) است. براي مثال اگر بخواهيم تابع اعداد تصادفي rand() را از سرفايل <cstdlib> به كار ببريم، بايد دستور پيشپردازندۀ زير را به ابتداي فايل برنامۀ اصلي اضافه کنيم:#include <cstdlib>
اسلاید 169: 3- توابع ساخت كاربرگرچه توابع بسيار متنوعي در کتابخانۀ C++ استاندارد وجود دارد ولي اين توابع براي بيشتر وظايف برنامهنويسي كافي نيستند. علاوه بر اين برنامهنويسان دوست دارند خودشان بتوانند توابعي را بسازند و استفاده نمايند.
اسلاید 170: مثال 3-5 تابع cube()يك مثال ساده از توابع ساخت كاربر:int cube(int x){ // returns cube of x: return x*x*x;}اين تابع، مكعب يك عدد صحيح ارسالي به آن را برميگرداند. بنابراين فراخواني cube(2) مقدار 8 را برميگرداند.
اسلاید 171: نوع بازگشتي تابع cube() که در بالا تعريف شد، int است. نام آن cube ميباشد و يک پارامتر از نوع int به نام x دارد. يعني تابع cube() يک مقدار از نوع int ميگيرد و پاسخي از نوع int تحويل ميدهد. بدنۀ تابع، يك بلوك كد است كه در ادامۀ عنوان آن ميآيد. بدنه شامل دستوراتي است كه بايد انجام شود تا نتيجۀ مورد نظر به دست آيد. بدنه شامل دستور return است كه پاسخ نهايي را به مكان فراخواني تابع برميگرداند. يك تابع ساخت كاربر دو قسمت دارد: 1-عنوان 2- بدنه. عنوان يك تابع به صورت زير است:(فهرست پارامترها) نام نوع بازگشتي مثال:int cube(int x){… بدنه تابع }
اسلاید 172: دستور return دو وظيفۀ عمده دارد. اول اين که اجراي تابع را خاتمه ميدهد و دوم اين که مقدار نهايي را به برنامۀ فراخوان باز ميگرداند. دستور return به شکل زير استفاده ميشود:return expression;به جاي expression هر عبارتي قرار ميگيرد که بتوان مقدار آن را به يک متغير تخصيص داد. نوع آن عبارت بايد با نوع بازگشتي تابع يکي باشد.عبارت int main() که در همۀ برنامهها استفاده کردهايم يک تابع به نام «تابع اصلي» را تعريف ميکند. نوع بازگشتي اين تابع از نوع int است. نام آن main است و فهرست پارامترهاي آن خالي است؛ يعني هيچ پارامتري ندارد.
اسلاید 173: وقتي يک تابع مورد نياز را ايجاد کرديد، فورا بايد آن تابع را با يک برنامۀ ساده امتحان کنيد. چنين برنامهاي برنامۀ آزمون ناميده ميشود. 4- برنامۀ آزمون برنامۀ آزمون يک برنامۀ موقتي است که بايد «سريع و کثيف» باشد؛ يعني: لازم نيست در آن تمام ظرافتهاي برنامهنويسي – مثل پيغامهاي خروجي، برچسبها و راهنماهاي خوانا – را لحاظ کنيد. تنها هدف اين برنامه، امتحان کردن تابع و بررسي صحت کار آن است.
اسلاید 174: مثال 4-5 يك برنامۀ آزمون براي تابع cube()کد زير شامل تابع cube() و برنامۀ آزمون آن است:int cube(int x){ // returns cube of x: return x*x*x;}int main(){ // tests the cube() function: int n=1; while (n != 0) { cin >> n; cout << tcube( << n << ) = << cube(n) << endl; }}برنامۀ حاضر اعداد صحيح را از ورودي ميگيرد و مكعب آنها را چاپ ميكند تا اين كه كاربر مقدار 0 را وارد كند.
اسلاید 175: هر عدد صحيحي که خوانده ميشود، با استفاده از کد cube(n) به تابع cube() فرستاده ميشود. مقدار بازگشتي از تابع، جايگزين عبارت cube(n) گشته و با استفاده از cout در خروجي چاپ ميشود. ميتوان رابطۀ بين تابع main() و تابع cube() را شبيه اين شکل تصور نمود: 5 nint5125cube()main()5 xintدقت كنيد كه تابع cube() در بالاي تابع main() تعريف شده زيرا قبل از اين كه تابعcube() در تابع main() به كار رود، كامپايلر C++ بايد در بارۀ آن اطلاع حاصل كند.
اسلاید 176: مثال 5-5 يك برنامۀ آزمون براي تابع max()تابع زير دو پارامتر دارد. اين تابع از دو مقدار فرستاده شده به آن، مقدار بزرگتر را برميگرداند:int max(int x, int y){ // returns larger of the two given integers: int z; z = (x > y) ? x : y ; return z;}int main(){ int m, n; do { cin >> m >> n; cout << tmax( << m << , << n << ) = << max(m,n) << endl; } while (m != 0);}
اسلاید 177: توابع ميتوانند بيش از يک دستور return داشته باشند. مثلا تابع max() را مانند اين نيز ميتوانستيم بنويسيم:int max(int x, int y){ // returns larger of the two given integers: if (x < y) return y; else return x;}در اين کد هر دستور return که زودتر اجرا شود مقدار مربوطهاش را بازگشت داده و تابع را خاتمه ميدهد. دستور return نوعي دستور پرش است (شبيه دستور break ) زيرا اجرا را به بيرون از تابع هدايت ميکند. اگرچه معمولا return در انتهاي تابع قرار ميگيرد، ميتوان آن را در هر نقطۀ ديگري از تابع قرار داد.
اسلاید 178: 5- اعلانها و تعاريف تابعبه دو روش ميتوان توابع را تعريف نمود:1-توابع قبل از تابع main() به طور كامل با بدنه مربوطه آورده شوند.2-راه ديگري که بيشتر رواج دارد اين گونه است که ابتدا تابع اعلان شود، سپس متن برنامۀ اصليmain() بيايد، پس از آن تعريف کامل تابع قرار بگيرد.
اسلاید 179: اعلان تابع با تعريف تابع تفاوت دارد. اعلان تابع، فقط عنوان تابع است که يک سميکولن در انتهاي آن قرار دارد.تعريف تابع، متن کامل تابع است که هم شامل عنوان است و هم شامل بدنه. اعلان تابع شبيه اعلان متغيرهاست. يک متغير قبل از اين که به کار گرفته شود بايد اعلان شود. تابع هم همين طور است با اين فرق که متغير را در هر جايي از برنامه ميتوان اعلان کرد اما تابع را بايد قبل از برنامۀ اصلي اعلان نمود.
اسلاید 180: همينها براي کامپايلر کافي است تا بتواند کامپايل برنامه را آغاز کند. سپس در زمان اجرا به تعريف بدنۀ تابع نيز احتياج ميشود که اين بدنه در انتهاي برنامه و پس از تابع main() قرار ميگيرد.در اعلان تابع فقط بيان ميشود که نوع بازگشتي تابع چيست، نام تابع چيست و نوع پارامترهاي تابع چيست.
اسلاید 181: فرق بين «آرگومان» و «پارامتر» :پارامترها متغيرهايي هستند که در فهرست پارامتر يک تابع نام برده ميشوند. پارامترها متغيرهاي محلي براي تابع محسوب ميشوند؛ يعني فقط در طول اجراي تابع وجود دارند. آرگومانها متغيرهايي هستند که از برنامۀ اصلي به تابع فرستاده ميشوند.
اسلاید 182: int max(int,int);int main(){ int m, n; do { cin >> m >> n; cout << tmax( << m << , << n << ) = << max(m,n) << endl; } while (m != 0);}int max(int x, int y){ if (x < y) return y; else return x;}مثال 6-5 تابعmax() با اعلان جدا از تعريف آناين برنامه همان برنامۀ آزمون تابع max() در مثال 5-6 است. اما اينجا اعلان تابع بالاي تابع اصلي ظاهر شده و تعريف تابع بعد از برنامۀ اصلي آمده است:توجه كنيد كه پارامترهاي x و y در بخش عنوان تعريف تابع آمدهاند (طبق معمول) ولي در اعلان تابع وجود ندارند.
اسلاید 183: اغلب اين طور است که تعريف و بدنۀ توابع در فايلهاي جداگانهاي قرار ميگيرد. اين فايلها به طور مستقل کامپايل1 ميشوند و سپس به برنامۀ اصلي که آن توابع را به کار ميگيرد الصاق2 ميشوند. 6- كامپايل جداگانۀ توابع توابع کتابخانۀ C++ استاندارد به همين شکل پيادهسازي شدهاند و هنگامي که يکي از آن توابع را در برنامههايتان به کار ميبريد بايد با دستور راهنماي پيشپردازنده، فايل آن توابع را به برنامهتان ضميمه کنيد. اين کار چند مزيت دارد:
اسلاید 184: 1- اولين مزيت «مخفيسازي اطلاعات» است. 2-مزيت ديگر اين است که توابع مورد نياز را ميتوان قبل از اين که برنامۀ اصلي نوشته شود، جداگانه آزمايش نمود. 3-سومين مزيت اين است که در هر زماني به راحتي ميتوان تعريف توابع را عوض کرد بدون اين که لازم باشد برنامۀ اصلي تغيير يابد.4-چهارمين مزيت هم اين است که ميتوانيد يک بار يک تابع را کامپايل و ذخيره کنيد و از آن پس در برنامههاي مختلفي از همان تابع استفاده ببريد.
اسلاید 185: int max(int x, int y){ if (x < y) return y; else return x;}max.cppتابع max() را به خاطر بياوريد. براي اين که اين تابع را در فايل جداگانهاي قرار دهيم، تعريف آن را در فايلي به نام max.cpp ذخيره ميکنيم. فايل max.cpp شامل کد زير است:
اسلاید 186: حال كافي است عبارت:#include <test.cpp> را به اول برنامه اصلي وقبل ازmain() اضافه كنيم:#include <test.cpp> int main(){ // tests the max() function: int m, n; do { cin >> m >> n; cout << tmax( << m << , << n << ) = << max(m,n) << endl; } while (m != 0);}
اسلاید 187: نحوۀ کامپايل کردن فايلها و الصاق آنها به يکديگر به نوع سيستم عامل و نوع کامپايلر بستگي دارد. در سيستم عامل ويندوز معمولا توابع را در فايلهايي از نوع DLL کامپايل و ذخيره ميکنند و سپس اين فايل را در برنامۀ اصلي احضار مينمايند. فايلهاي DLL را به دو طريق ايستا و پويا ميتوان مورد استفاده قرار داد. براي آشنايي بيشتر با فايلهاي DLL به مرجع ويندوز و کامپايلرهاي C++ مراجعه کنيد.
اسلاید 188: 6- متغيرهاي محلي، توابع محليمتغير محلي، متغيري است که در داخل يک بلوک اعلان گردد. اين گونه متغيرها فقط در داخل همان بلوکي که اعلان ميشوند قابل دستيابي هستند. چون بدنۀ تابع، خودش يک بلوک است پس متغيرهاي اعلان شده در يک تابع متغيرهاي محلي براي آن تابع هستند. اين متغيرها فقط تا وقتي که تابع در حال کار است وجود دارند. پارامترهاي تابع نيز متغيرهاي محلي محسوب ميشوند.
اسلاید 189: * مثال 7-5 تابع فاكتوريلاعداد فاكتوريل را در مثال 8-5 ديديم. فاكتوريل عدد صحيح n برابر است با:n! = n(n-1)(n-2)..(3)(2)(1)تابع زير، فاکتوريل عدد n را محاسبه ميکند:long fact(int n){ //returns n! = n*(n-1)*(n-2)*...*(2)*(1) if (n < 0) return 0; int f = 1; while (n > 1) f *= n--; return f;}اين تابع دو متغير محلي دارد: n و f پارامتر n يک متغير محلي است زيرا در فهرست پارامترهاي تابع اعلان شده و متغير f نيز محلي است زيرا درون بدنۀ تابع اعلان شده است.
اسلاید 190: همان گونه که متغيرها ميتوانند محلي باشند، توابع نيز ميتوانند محلي باشند. يک تابع محلي تابعي است که درون يک تابع ديگر به کار رود. با استفاده از چند تابع ساده و ترکيب آنها ميتوان توابع پيچيدهتري ساخت. به مثال زير نگاه کنيد. تابع محلي در رياضيات، تابع جايگشت را با p(n,k) نشان ميدهند. اين تابع بيان ميکند که به چند طريق ميتوان k عنصر دلخواه از يک مجموعۀ n عنصري را کنار يکديگر قرار داد. براي اين محاسبه از رابطۀ زير استفاده ميشود:
اسلاید 191: پس به 12 طريق ميتوانيم دو عنصر دلخواه از يک مجموعۀ چهار عنصري را کنار هم بچينيم. براي دو عنصر از مجموعۀ {1, 2, 3, 4} حالتهاي ممکن عبارت است از:12, 13, 14, 21, 23, 24, 31, 32, 34, 41, 42, 43كد زير تابع جايگشت را پيادهسازي ميكند:long perm(int n, int k){// returns P(n,k), the number of the permutations of k from n: if (n < 0) || k < 0 || k > n) return 0; return fact(n)/fact(n-k);}اين تابع، خود از تابع ديگري که همان تابع فاکتوريل است استفاده کرده است. شرط به کار رفته در دستور if براي محدود کردن حالتهاي غير ممکن استفاده شده است. در اين حالتها، تابع مقدار 0 را برميگرداند تا نشان دهد که يک ورودي اشتباه وجود داشته است.
اسلاید 192: برنامۀ آزمون براي تابع perm() در ادامه آمده است:long perm(int,int);// returns P(n,k), the number of permutations of k from n:int main(){ // tests the perm() function: for (int i = -1; i < 8; i++) { for (int j= -1; j <= i+1; j++) cout << << perm(i,j); cout << endl; }}0 00 1 00 1 1 00 1 2 2 00 1 3 6 6 00 1 4 12 24 24 00 1 5 20 60 120 120 00 1 6 30 120 360 720 720 00 1 7 42 210 840 2520 5040 5040 0
اسلاید 193: 7- تابع voidلازم نيست يك تابع حتما مقداري را برگرداند. در C++ براي مشخص کردن چنين توابعي از کلمۀ کليدي void به عنوان نوع بازگشتي تابع استفاده ميکنند يک تابع void تابعي است که هيچ مقدار بازگشتي ندارد. از آنجا كه يك تابع void مقداري را برنميگرداند، نيازي به دستور return نيست ولي اگر قرار باشد اين دستور را در تابع void قرار دهيم، بايد آن را به شکل تنها استفاده کنيم بدون اين که بعد از کلمۀ return هيچ چيز ديگري بيايد:return;در اين حالت دستور return فقط تابع را خاتمه ميدهد.
اسلاید 194: 8- توابع بوليدر بسياري از اوقات لازم است در برنامه، شرطي بررسي شود. اگر بررسي اين شرط به دستورات زيادي نياز داشته باشد، بهتر است که يک تابع اين بررسي را انجام دهد. اين کار مخصوصا هنگامي که از حلقهها استفاده ميشود بسيار مفيد است.توابع بولي فقط دو مقدار را برميگردانند: true يا false . اسم توابع بولي را معمولا به شکل سوالي انتخاب ميکنند زيرا توابع بولي هميشه به يک سوال مفروض پاسخ بلي يا خير ميدهند.
اسلاید 195: مثال 10-5 تابعي كه اول بودن اعداد را بررسي ميكندکد زير يك تابع بولي است كه تشخيص ميدهد آيا عدد صحيح ارسال شده به آن، اول است يا خير:bool isPrime(int n){ // returns true if n is prime, false otherwise: float sqrtn = sqrt(n); if (n < 2) return false; // 0 and 1 are not primes if (n < 4) return true; // 2 and 3 are the first primes if (n%2 == 0) return false; // 2 is the only even prime for (int d=3; d <= sqrtn; d += 2) if (n%d == 0) return false; // n has a nontrivial divisor return true; // n has no nontrivial divisors}
اسلاید 196: 9- توابع ورودي/خروجي (I/O)بخشهايي از برنامه که به جزييات دست و پا گير ميپردازد و خيلي به هدف اصلي برنامه مربوط نيست را ميتوان به توابع سپرد. در چنين شرايطي سودمندي توابع محسوستر ميشود. فرض کنيد نرمافزاري براي سيستم آموزشي دانشگاه طراحي کردهايد که سوابق تحصيلي دانشجويان را نگه ميدارد. در اين نرمافزار لازم است که سن دانشجو به عنوان يکي از اطلاعات پروندۀ دانشجو وارد شود. اگر وظيفۀ دريافت سن را به عهدۀ يک تابع بگذاريد، ميتوانيد جزيياتي از قبيل کنترل ورودي معتبر، يافتن سن از روي تاريخ تولد و ... را در اين تابع پيادهسازي کنيد بدون اين که از مسير برنامۀ اصلي منحرف شويد.
اسلاید 197: قبلا نمونهاي از توابع خروجي را ديديم. تابع PrintDate() در مثال 9-5 هيچ چيزي به برنامۀ اصلي برنميگرداند و فقط براي چاپ نتايج به کار ميرود. اين تابع نمونهاي از توابع خروجي است؛ يعني توابعي که فقط براي چاپ نتايج به کار ميروند و هيچ مقدار بازگشتي ندارند. توابع ورودي نيز به همين روش کار ميکنند اما در جهت معکوس. يعني توابع ورودي فقط براي دريافت ورودي و ارسال آن به برنامۀ اصلي به کار ميروند و هيچ پارامتري ندارند. مثال بعد يک تابع ورودي را نشان ميدهد.
اسلاید 198: مثال 11-5 تابعي براي دريافت سن كاربرتابع سادۀ زير، سن کاربر را درخواست ميکند و مقدار دريافت شده را به برنامۀ اصلي ميفرستد. اين تابع تقريبا هوشمند است و هر عدد صحيح ورودي غير منطقي را رد ميکند و به طور مکرر درخواست ورودي معتبر ميکند تا اين که يک عدد صحيح در محدودۀ 7 تا 120 دريافت دارد: int age(){ // prompts the user to input his/her age and returns that value: int n; while (true) { cout << How old are you: ; cin >> n; if (n < 0) cout << atYour age could not be negative.; else if (n > 120) cout << atYou could not be over 120.; else return n; cout << ntTry again.n; }}
اسلاید 199: يك برنامۀ آزمون و خروجي حاصل از آن در ادامه آمده است:int age()int main(){ // tests the age() function: int a = age(); cout << nYou are << a << years old.n;}How old are you? 125 You could not be over 120 Try again.How old are you? -3 Your age could not be negative Try again.How old are you? 99You are 99 years old.
اسلاید 200: تا اين لحظه تمام پارامترهايي كه در توابع ديديم به طريق مقدار ارسال شدهاند. يعني ابتدا مقدار متغيري که در فراخواني تابع ذکر شده برآورد ميشود و سپس اين مقدار به پارامترهاي محلي تابع فرستاده ميشود. مثلا در فراخواني cube(x) ابتدا مقدار x برآورد شده و سپس اين مقدار به متغير محلي n در تابع فرستاده ميشود و پس از آن تابع کار خويش را آغاز ميکند. در طي اجراي تابع ممکن است مقدار n تغيير کند اما چون n محلي است هيچ تغييري روي مقدار x نميگذارد.
اسلاید 201: پس خود x به تابع نميرود بلکه مقدار آن درون تابع کپي ميشود. تغيير دادن اين مقدار کپي شده درون تابع هيچ تاثيري بر x اصلي ندارد. به اين ترتيب تابع ميتواند مقدار x را بخواند اما نميتواند مقدار x را تغيير دهد. به همين دليل به x يک پارامتر «فقط خواندني» ميگويند. وقتي ارسال به وسيلۀ مقدار باشد، هنگام فراخواني تابع ميتوان از عبارات استفاده کرد. مثلا تابع cube() را ميتوان به صورتcube(2*x-3) فراخواني کرد يا به شکل cube(2*sqrt(x)-cube(3)) فراخواني نمود. در هر يک از اين حالات، عبارت درون پرانتز به شکل يک مقدار تکي برآورد شده و حاصل آن مقدار به تابع فرستاده ميشود.
اسلاید 202: 10- ارسال به طريق ارجاع (آدرس)ارسال به طريق مقدار باعث ميشود که متغيرهاي برنامۀ اصلي از تغييرات ناخواسته در توابع مصون بمانند. اما گاهي اوقات عمدا ميخواهيم اين اتفاق رخ دهد. يعني ميخواهيم که تابع بتواند محتويات متغير فرستاده شده به آن را دستکاري کند. در اين حالت از ارسال به طريق ارجاع استفاده ميکنيم.
اسلاید 203: براي اين که مشخص کنيم يک پارامتر به طريق ارجاع ارسال ميشود، علامت را به نوع پارامتر در فهرست پارامترهاي تابع اضافه ميکنيم. اين باعث ميشود که تابع به جاي اين که يک کپي محلي از آن آرگومان ايجاد کند، خود آرگومان محلي را به کار بگيرد. به اين ترتيب تابع هم ميتواند مقدار آرگومان فرستاده شده را بخواند و هم ميتواند مقدار آن را تغيير دهد. در اين حالت آن پارامتر يک پارامتر «خواندني-نوشتني» خواهد بود. &
اسلاید 204: * مثال 12-5 تابع swap()تابع كوچك زير در مرتب کردن دادهها کاربرد فراوان دارد:void swap(float& x, float& y){ // exchanges the values of x and y: float temp = x; x = y; y = temp;}هر تغييري که روي پارامتر خواندني-نوشتني در تابع صورت گيرد به طور مستقيم روي متغير برنامۀ اصلي اعمال ميشود. به مثال زير نگاه کنيد.هدف اين تابع جابجا کردن دو عنصري است که به آن فرستاده ميشوند. براي اين منظور پارامترهاي x و y به صورت پارامترهاي ارجاع تعريف شدهاند:float& x, float& y
اسلاید 205: عملگر ارجاع & موجب ميشود كه به جاي x و y آرگومانهاي ارسالي قرار بگيرند. برنامۀ آزمون و اجراي آزمايشي آن در زير آمده است:void swap(float&, float&)// exchanges the values of x and y:int main(){ // tests the swap() function: float a = 55.5, b = 88.8; cout << a = << a << , b = << b << endl; swap(a,b); cout << a = << a << , b = << b << endl;}a = 55.5, b = 88.8a = 88.8, b = 55.5
اسلاید 206: وقتي فراخواني swap(a,b) اجرا ميشود، x به a اشاره ميکند و y به b. سپس متغير محلي temp اعلان ميشود و مقدار x (که همان a است) درون آن قرار ميگيرد. پس از آن مقدار y (که همان b است) درون x (يعني a) قرار ميگيرد و آنگاه مقدار temp درون y (يعني b) قرار داده ميشود. نتيجۀ نهايي اين است که مقادير a و b با يکديگر جابجا مي شوند. شکل مقابل نشان ميدهد که چطور اين جابجايي رخ ميدهد: 55.5afloatmain()xfloat&88.8bfloatyfloat&88.8afloatswap()main()xfloat&55.5bfloatyfloat&55.5tempfloatهنگام فراخواني تابع swap(a,b)بعد از بازگشتswap()
اسلاید 207: به اعلان تابع swap() دقت کنيد:void swap(float&, float&)اين اعلان شامل عملگر ارجاع & براي هر پارامتر است. برنامهنويسان c عادت دارند که عملگر ارجاع & را به عنوان پيشوند نام متغير استفاده کنند (مثلfloat &x) در C++ فرض ميکنيم عملگر ارجاع & پسوند نوع است (مثل float& x) به هر حال کامپايلر هيچ فرقي بين اين دو اعلان نميگذارد و شکل نوشتن عملگر ارجاع کاملا اختياري و سليقهاي است.
اسلاید 208: مثال 13-5 ارسال به طريق مقدار و ارسال به طريق ارجاعاين برنامه، تفاوت بين ارسال به طريق مقدار و ارسال به طريق ارجاع را نشان ميدهد:void f(int,int&);int main(){ int a = 22, b = 44; cout << a = << a << , b = << b << endl; f(a,b); cout << a = << a << , b = << b << endl; f(2*a-3,b); cout << a = << a << , b = << b << endl;}void f(int x , int& y){ x = 88; y = 99;}a = 22, b = 44a = 22, b = 99a = 22, b = 99تابع f() دو پارامتر دارد که اولي به طريق مقدار و دومي به طريق ارجاع ارسال ميشود. فراخواني f(a,b) باعث ميشود که a از طريق مقدار به x ارسال شود و b از طريق ارجاع به y فرستاده شود.
اسلاید 209: 22aintmain()22xint44bintyint&f()هنگام فراخواني تابع f(a,b) شکل زير نحوۀ کار تابع f() را نشان ميدهد.22aintmain()88xxint99bintyint&f()بعد از بازگشت
اسلاید 210: ارسال از طريق ارجاعارسال از طريق مقدارint& x;int x;پارامتر x يک ارجاع استپارامتر x يک متغير محلي استx مترادف با آرگومان استx يک کپي از آرگومان استميتواند محتويات آرگومان را تغيير دهدتغيير محتويات آرگومان ممکن نيستآرگومان ارسال شده از طريق ارجاع فقط بايد يک متغير باشدآرگومان ارسال شده از طريق مقدار ميتواند يک ثابت، يک متغير يا يک عبارت باشدآرگومان خواندني-نوشتني استآرگومان فقط خواندني استدر جدول زير خلاصۀ تفاوتهاي بين ارسال از طريق مقدار و ارسال از طريق ارجاع آمده است.
اسلاید 211: يكي از مواقعي كه پارامترهاي ارجاع مورد نياز هستند جايي است كه تابع بايد بيش از يك مقدار را بازگرداند. دستور return فقط ميتواند يك مقدار را برگرداند. بنابراين اگر بايد بيش از يك مقدار برگشت داده شود، اين كار را پارامترهاي ارجاع انجام ميدهند.
اسلاید 212: * مثال 14-5 بازگشت بيشتر از يك مقدارتابع زير از طريق دو پارامتر ارجاع، دو مقدار را بازميگرداند: area و circumference (محيط و مساحت) براي دايرهاي که شعاع آن عدد مفروض r است:void ComputeCircle(double& area, double& circumference, double r){ // returns the area and circumference of a circle with radius r: const double PI = 3.141592653589793; area = PI*r*r; circumference = 2*PI*r;}
اسلاید 213: برنامۀ آزمون تابع فوق و يک اجراي آزمايشي آن در شکل زير نشان داده شده است:void ComputerCircle(double&, double&, double);// returns the area and circumference of a circle with radius r;int main(){ // tests the ComputeCircle() function: double r, a, c; cout << Enter radius: ; cin >> r; ComputeCircle(a, c, r); cout << area = << a << , circumference = << c << endl;}
اسلاید 214: 12- ارسال از طريق ارجاع ثابتارسال پارامترها به طريق ارجاع دو خاصيت مهم دارد: اول اين که تابع ميتواند روي آرگومان واقعي تغييراتي بدهد دوم اين که از اشغال بيمورد حافظه جلوگيري ميشود. روش ديگري نيز براي ارسال آرگومان وجود دارد: ارسال از طريق ارجاع ثابت. اين روش مانند ارسال از طريق ارجاع است با اين فرق که تابع نميتواند محتويات پارامتر ارجاع را دستکاري نمايد و فقط اجازۀ خواندن آن را دارد. براي اين که پارامتري را از نوع ارجاع ثابت اعلان کنيم بايد عبارت const را به ابتداي اعلان آن اضافه نماييم.
اسلاید 215: مثال 15-5 ارسال از طريق ارجاع ثابتسه طريقه ارسال پارامتر در تابع زير به کار رفته است:void f(int x, int& y, const int& z){ x += z; y += z; cout << x = << x << , y = << y << , z = << z << endl;}در تابع فوق اولين پارامتر يعني x از طريق مقدار ارسال ميشود، دومين پارامتر يعني y از طريق ارجاع و سومين پارامتر نيز از طريق ارجاع ثابت.
اسلاید 216: برنامۀ آزمون و يک اجراي آزمايشي از مثال قبل:void f(int, int&, const int&);int main(){ // tests the f() function: int a = 22, b = 33, c = 44; cout << a = << a << , b = << b << , c = << c << endl; f(a,b,c); cout << a = << a << , b = << b << , c = << c << endl; f(2*a-3,b,c); cout << a = << a << , b = << b << , c = << c << endl;}a = 22, b = 33, c = 44x = 66, y = 77, z = 44a = 22, b = 77, c = 44x = 85, y = 121, z = 44a = 22, b = 121, c = 44تابع فوق پارامترهاي x و y را ميتواند تغيير دهد ولي قادر نيست پارامتر z را تغيير دهد. تغييراتي که روي x صورت ميگيرد اثري روي آرگومان a نخواهد داشت زيرا a از طريق مقدار به تابع ارسال شده. تغييراتي که روي y صورت ميگيرد روي آرگومان b هم تاثير ميگذارد زيرا b از طريق ارجاع به تابع فرستاده شده.
اسلاید 217: ارسال به طريق ارجاع ثابت بيشتر براي توابعي استفاده ميشود که عناصر بزرگ را ويرايش ميکنند مثل آرايهها يا نمونۀ کلاسها که در جلسههاي بعدي توضيح آنها آمده است. عناصري که از انواع اصلي هستند (مثل int يا float) به طريق مقدار ارسال ميشوند به شرطي که قرار نباشد تابع محتويات آنها را دستکاري کند.
اسلاید 218: 13- توابع بيواسطهتابعي که به شکل بيواسطه تعريف ميشود، ظاهري شبيه به توابع معمولي دارد با اين فرق که عبارت inline در اعلان و تعريف آن قيد شده است.مثال 16-5 تابع cube() به شکل بيواسطهاين همان تابع cube() مثال 3-5 است:inline int cube(int x){ // returns cube of x: return x*x*x;}تنها تفاوت اين است كه كلمۀ كليدي inline در ابتداي عنوان تابع ذکر شده. اين عبارت به كامپايلر ميگويد كه در برنامه به جاي cube(n) کد واقعي (n)*(n)*(n) را قرار دهد.
اسلاید 219: . به برنامۀ آزمون زير نگاه کنيد:int main(){ // tests the cube() function: cout << cube(4) << endl; int x, y; cin >> x; y = cube(2*x-3);}اين برنامه هنگام کامپايل به شکل زير درميآيد، گويي اصلا تابعي وجود نداشته:int main(){ // tests the cube() function: cout << (4) * (4) * (4) << endl; int x, y; cin >> x; y = (2*x-3) * (2*x-3) * (2*x-3);}وقتي كامپايلر کد واقعي تابع را جايگزين فراخواني آن ميکند، ميگوييم که تابع بيواسطه، باز ميشود.احتياط: استفاده از توابع بيواسطه ميتواند اثرات منفي داشته باشد. مثلا اگر يک تابع بيواسطه داراي 40 خط کد باشد و اين تابع در 26 نقطه مختلف از برنامۀ اصلي فراخواني شود، هنگام کامپايل بيش از هزار خط کد به برنامۀ اصلي افزوده ميشود. همچنين تابع بيواسطه ميتواند قابليت انتقال برنامۀ شما را روي سيستمهاي مختلف کاهش دهد.
اسلاید 220: 14- چندشکلي توابعدر C++ ميتوانيم چند تابع داشته باشيم که همگي يک نام دارند. در اين حالت ميگوييم که تابع مذکور، چندشکلي دارد. شرط اين کار آن است که فهرست پارامترهاي اين توابع با يکديگر تفاوت داشته باشد. يعني تعداد پارامترها متفاوت باشد يا دست کم يکي از پارامترهاي متناظر هم نوع نباشند.
اسلاید 221: مثال 17-5 چندشکلي تابع max()در مثال 3-5 تابع max() را تعريف کرديم. حالا توابع ديگري با همان نام ولي شکلي متفاوت تعريف ميکنيم و همه را در يک برنامه به کار ميگيريم:int max(int, int);int max(int, int, int);int max(double, double);int main(){ cout << max(99,77) << << max(55,66,33) << << max(44.4,88.8);}
اسلاید 222: int max(int x, int y){ // returns the maximum of the two given integers: return (x > y ? x : y);}int max(int x, int y, int z){ // returns the maximum of the three given integers: int m = (x > y ? x : y); // m = max(x , y) return ( z > m ? z : m);}int max(double x, double y){ // return the maximum of the two given doubles: return (x>y ? x : y);}
اسلاید 223: در اين برنامه سه تابع با نام max() تعريف شده است. وقتي تابع max() در جايي از برنامه فراخواني ميشود، کامپايلر فهرست آرگومان آن را بررسي ميکند تا بفهمد که کدام نسخه از max بايد احضار شود. مثلا در اولين فراخواني تابع max() دو آرگومان int ارسال شده، پس نسخهاي که دو پارامتر int در فهرست پارامترهايش دارد فراخواني ميشود. اگر اين نسخه وجود نداشته باشد، کامپايلر intها را به double ارتقا ميدهد و سپس نسخهاي که دو پارامتر double دارد را فرا ميخواند.
اسلاید 224: 14- تابع main() برنامههايي که تا کنون نوشتيم همه داراي تابعي به نام main() هستند. منطق C++ اين طور است که هر برنامه بايد داراي تابعي به نام main() باشد. در حقيقت هر برنامه کامل، از يک تابع main() به همراه توابع ديگر تشکيل شده است که هر يک از اين توابع به شکل مستقيم يا غير مستقيم از درون تابع main() فراخواني ميشوند.
اسلاید 225: خود برنامه با فراخواني تابع main() شروع ميشود. چون اين تابع يک نوع بازگشتي int دارد، منطقي است که بلوک تابع main() شامل دستور return 0; باشد هرچند که در برخي از کامپايلرهاي C++ اين خط اجباري نيست و ميتوان آن را ذکر نکرد.مقدار صحيحي که با دستور return به سيستم عامل برميگردد بايد تعداد خطاها را شمارش کند. مقدار پيشفرض آن 0 است به اين معنا که برنامه بدون خطا پايان گرفته است. با استفاده از دستور return ميتوانيم برنامه را به طور غيرمعمول خاتمه دهيم.
اسلاید 226: مثال 18-5 استفاده از دستور return براي پايان دادن به يك برنامهint main(){ // prints the quotient of two input integers: int n, d; cout << Enter two integers: ; cin >> n >> d; if (d = = 0) return 0; cout << n << / << d << = << n/d << endl;}Enter two integers: 99 1799/17 = 5 دستور return تابع فعلي را خاتمه ميدهد و کنترل را به فراخواننده بازميگرداند. به همين دليل است که اجراي دستور return در تابع main() کل برنامه را خاتمه ميدهد.
اسلاید 227: چهار روش وجود دارد که بتوانيم برنامه را به شکل غيرمعمول (يعني قبل از اين که اجرا به پايان بلوک اصلي برسد) خاتمه دهيم:1 - استفاده از دستور return 2 - فراخواني تابع exit()3 - فراخواني تابع abort()4 – ايجاد يک حالت استثنا اين تابع در سرفايل <cstdlib> تعريف شده است. تابع exit() براي خاتمه دادن به کل برنامه در هر تابعي غير از تابع main() مفيد است.
اسلاید 228: مثال 19-5 استفاده از تابع exit() براي پايان دادن به برنامه#include <cstdlib> // defines the exit() function#include <iostream> // defines thi cin and cout objectsusing namespace std;double reciprocal(double x);int main(){ double x; cin >> x; cout << reciprocal(x);}double reciprocal(double x)1 – Exception{ // returns the reciprocal of x: if (x = = 0) exit(1); // terminate the program return 1.0/x; }دراين برنامۀ اگر كاربر عدد 0 را وارد کند، تابع reciprocal() خاتمه مييابد و برنامه بدون هيچ مقدار چاپي به پايان ميرسد
اسلاید 229: 15- آرگومانهاي پيشفرضدر C++ ميتوان تعداد آرگومانهاي يک تابع را در زمان اجرا به دلخواه تغيير داد. اين امر با استفاده از آرگومانهاي اختياري و مقادير پيشفرض امکانپذير است.براي اين که به يک پارامتر مقدار پيشفرض بدهيم بايد آن مقدار را در فهرست پارامترهاي تابع و جلوي پارامتر مربوطه به همراه علامت مساوي درج کنيم. به اين ترتيب اگر هنگام فراخواني تابع، آن آرگومان را ذکر نکنيم، مقدار پيشفرض آن در محاسبات تابع استفاده ميشود. به همين خاطر به اين گونه آرگومانها، آرگومان اختياري ميگويند.
اسلاید 230: double p(double, double, double=0, double=0, double=0);int main(){ // tests the p() function: double x = 2.0003; cout << p(x,7) = << p(x,7) << endl; cout << p(x,7,6) = << p(x,7,6) << endl; cout << p(x,7,6,5) = << p(x,7,6,5) << endl; cout << p(x,7,6,5,4) = << p(x,7,6,5,4) << endl;}double p(double x, double a0, double a1=0, double a2=0, double a3=0){ // returns a0 + a1*x + a2*x^2 + a3*x^3: return a0 + (a1 + (a2 + a3*x)*x)*x;} مثال 20-5 آرگومانهاي پيشفرضبرنامۀ زير حاصل چند جملهاي درجه سوم را پيدا ميکند. براي محاسبۀ اين مقدار از الگوريتم هورنر استفاده شده. به اين شکل که براي کارايي بيشتر، محاسبه به صورت دستهبندي ميشود: p(x,7) = 7p(x,7,6) = 19.0018p(x,7,6,5) = 39.00781 – Defaultp(x,7,6,5,4) = 71.0222
اسلاید 231: دقت کنيد که پارامترهايي که مقدار پيشفرض دارند بايد در فهرست پارامترهاي تابع بعد از همۀ پارامترهاي اجباري قيد شوند مثل:void f( int a, int b, int c=4, int d=7, int e=3); // OKvoid g(int a, int b=2, int c=4, int d, int e=3); // ERRORهمچنين هنگام فراخواني تابع، آرگومانهاي ذکر شده به ترتيب از چپ به راست تخصيص مييابند و پارامترهاي بعدي با مقدار پيشفرض پر ميشوند. مثلا در تابع p() که در بالا قيد شد، فراخواني p(8.0,7,6) باعث ميشود که پارامتر x مقدار 8.0 را بگيرد سپس پارامتر a0 مقدار 7 را بگيرد و سپس پارامتر a1 مقدار 6 را بگيرد. پارامترهاي a2 و a3 مقدار پيشفرضشان را خواهند داشت. اين ترتيب را نميتوانيم به هم بزنيم. مثلا نميتوانيم تابع را طوري فرا بخوانيم که پارامترهاي x و a0 و a3 مستقيما مقدار بگيرند ولي پارامترهاي a1 و a2 مقدار پيشفرضشان را داشته باشند.
اسلاید 232: پايان جلسه پنجم
اسلاید 233: جلسه ششم«آرايهها»
اسلاید 234: 1- پردازش آرايهها2- مقداردهي آرايهها3- ايندكس بيرون از حدود آرايه4- ارسال آرايه به تابع5- الگوريتم جستجوي خطي6- مرتبسازي حبابي7- الگوريتم جستجوي دودوييآنچه در اين جلسه مي خوانيد:›››
اسلاید 235: 8- استفاده از انواع شمارشي در آرايه 9- تعريف انواع 10 -آرايههاي چند بعدي
اسلاید 236: هدف کلي:شناخت و معرفي آرايهها و مزيت و طريقۀ بهکارگيري آنها هدفهاي رفتاري:انتظار ميرود پس از پايان اين جلسه بتوانيد:- علت استفاده از آرايهها را بدانيد و بتوانيد آنها را در برنامهها به کار ببريد.- آرايههاي «يکبعدي» و «چندبعدي» را تعريف کنيد.- مفهوم «ايندکس» را بدانيد و خطاي «اثر همسايگي» را تعريف و شناسايي کنيد.- طريقۀ ارسال آرايه به توابع را بدانيد.- «جستجوي خطي» و «جستجوي دودويي» را به اختصار شرح دهيد.- «مرتبسازي حبابي» را به اختصار شرح دهيد.
اسلاید 237: مقدمه:در برنامههايي که دادههاي فراواني را پردازش ميکنند استفاده از متغيرهاي معمولي کار عاقلانهاي نيست زيرا در بسياري از اين برنامهها «پردازش دستهاي» صورت ميگيرد به اين معني که مجموعهاي از دادههاي مرتبط با هم در حافظه قرار داده ميشود و پس از پردازش، کل اين مجموعه از حافظه خارج ميشود و مجموعۀ بعدي در حافظه بارگذاري ميشود. اگر قرار باشد براي اين کار از متغيرهاي معمولي استفاده شود بيشتر وقت برنامهنويس صرف پر و خالي کردن انبوهي از متغيرها ميشود. به همين دليل در بيشتر زبانهاي برنامهنويسي «آرايهها» تدارک ديده شدهاند. آرايه را ميتوان متغيري تصور کرد که يک نام دارد ولي چندين مقدار را به طور همزمان نگهداري مينمايد.
اسلاید 238: يک آرايه، يك زنجيره از متغيرهايي است كه همه از يك نوع هستند. به اين متغيرها «اعضاي آرايه» ميگويند. هر عضو آرايه با يک شماره مشخص ميشود که به اين شماره «ايندکس» يا «زيرنويس» ميگويند عناصر يک آرايه در خانههاي پشت سر هم در حافظه ذخيره ميشوند. به اين ترتيب آرايه را ميتوان بخشي از حافظه تصور کرد که اين بخش خود به قسمتهاي مساوي تقسيم شده و هر قسمت به يک عنصر تعلق دارد.
اسلاید 239: شکل مقابل آرايۀ a که پنج عنصر دارد را نشان ميدهد. عنصر a[0] حاوي مقدار 17.5 و عنصر a[1] حاوي 19.0 و عنصر a[4] حاوي مقدار 18.0 است. 017.50119.00216.75315.00418.00
اسلاید 240: 2- پردازش آرايههاآرايهها را ميتوان مثل متغيرهاي معمولي تعريف و استفاده کرد. با اين تفاوت که آرايه يک متغير مرکب است و براي دستيابي به هر يک از خانههاي آن بايد از ايندکس استفاده نمود. مثال 1-6 دستيابي مستقيم به عناصر آرايهبرنامۀ سادۀ زير يک آرايۀ سه عنصري را تعريف ميکند و سپس مقاديري را در آن قرار داده و سرانجام اين مقادير را چاپ ميکند:int main(){ int a[3]; a[2] = 55; a[0] = 11; a[1] = 33; cout << a[0] = << a[0] << endl; cout << a[1] = << a[1] << andl; cout << a[2] = << a[2] << endl;}a[0] = 11a[1] = 33a[2] = 55
اسلاید 241: نحو کلي براي اعلان آرايه به شکل زير است:type array_name[array_size];عبارت type نوع عناصر آرايه را مشخص ميکند. array_name نام آرايه است . array_size تعداد عناصر آرايه را نشان ميدهد. اين مقدار بايد يک عدد ثابت صحيح باشد و حتما بايد داخل کروشه [] قرار بگيرد.
اسلاید 242: در C++ ميتوانيم يک آرايه را با استفاده از فهرست مقداردهي، اعلان و مقدارگذاري کنيم:float a[] = {22.2,44.4,66.6};به اين ترتيب مقادير داخل فهرست به همان ترتيبي که چيده شدهاند درون عناصر آرايه قرار ميگيرند. اندازه آرايه نيز برابر با تعداد عناصر موجود در فهرست خواهد بود.پس همين خط مختصر، آرايهاي از نوع float و با نام a و با تعداد سه عنصر اعلان کرده و هر سه عنصر را با مقدارهاي درون فهرست، مقداردهي ميکند. 3- مقداردهي آرايههاa022.2144.4266.6
اسلاید 243: مثال 3-6 مقداردهي آرايه با استفاده از فهرست مقداردهيبرنامۀ زير، آرايۀ a را مقداردهي کرده و سپس مقدار هر عنصر را چاپ ميكند:int main(){ float a[] = { 22.2, 44.4, 66.6 }; int size = sizeof(a)/sizeof(float); for (int i=0; i<size; i++) cout << ta[ << i << ] = << a[i] << endl;} a[0] = 22.2 a[1] = 44.4 a[2] = 66.6
اسلاید 244: هنگام استفاده از فهرست مقداردهي براي اعلان آرايه، ميتوانيم تعداد عناصر آرايه را هم به طور صريح ذکر کنيم. در اين صورت اگر تعداد عناصر ذکر شده از تعداد عناصر موجود در فهرست مقداردهي بيشتر باشد، خانههاي بعدي با مقدار صفر پر ميشوند:float a[7] = { 55.5, 66.6, 77.7 };a055.5166.6277.730.040.050.060.0دقت کنيد که تعداد مقادير موجود در فهرست مقداردهي نبايد از تعداد عناصر آرايه بيشتر باشد:float a[3] = { 22.2, 44.4, 66.6, 88.8 }; // ERROR: too many values!
اسلاید 245: يك آرايه را ميتوانيم به طور کامل با صفر مقداردهي اوليه کنيم. براي مثال سه اعلان زير با هم برابرند:float a[ ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };float a[9] = { 0, 0 };float a[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };اما مطلب فوق اصلا به اين معني نيست که از فهرست مقداردهي استفاده نشود. درست مثل يک متغير معمولي، اگر يک آرايه مقداردهي اوليه نشود، عناصر آن حاوي مقادير زباله خواهد بود.
اسلاید 246: مثال 5-6 يك آرايۀ مقداردهي نشدهبرنامۀ زير، آرايۀ a را اعلان ميکند ولي مقداردهي نميكند. با وجود اين، مقادير موجود در آن را چاپ ميكند:int main(){ const int SIZE=4; // defines the size N for 4 elements float a[SIZE]; // declares the arrays elements as float for (int i=0; i<SIZE; i++) cout << ta[ << i << ] = << a[i] << endl;}a[0] = 6.01838e-39a[1] = 9.36651e-39a[2] = 6.00363e-39a[3] = 0
اسلاید 247: آرايهها را ميتوان با استفاده از عملگر جايگزيني مقداردهي کرد اما نميتوان مقدار آنها را به يکديگر تخصيص داد:float a[7] = { 22.2, 44.4, 66.6 };float b[7] = { 33.3, 55.5, 77.7 };b = a; // ERROR: arrays cannot be assigned!همچنين نميتوانيم يك آرايه را به طور مستقيم براي مقداردهي به آرايۀ ديگر استفاده كنيم:float a[7] = { 22.2, 44.4, 66.6 };float b[7] = a; // ERROR: arrays cannot be used as nitializers!
اسلاید 248: 4- ايندكس بيرون از حدود آرايهدر بعضي از زبانهاي برنامهنويسي، ايندکس آرايه نميتواند از محدودۀ تعريف شده براي آن بيشتر باشد. براي مثال در پاسکال اگر آرايۀ a با تعداد پنج عنصر تعريف شده باشد و آنگاه a[7] دستيابي شود، برنامه از کار ميافتد. اين سيستم حفاظتي در C++ وجود ندارد. مثال بعدي نشان ميدهد که ايندکس يک آرايه هنگام دستيابي ميتواند بيشتر از عناصر تعريف شده براي آن باشد و باز هم بدون اين که خطايي گرفته شود، برنامه ادامه يابد.
اسلاید 249: مثال 6-6 تجاوز ايندکس آرايه از محدودۀ تعريف شده براي آنبرنامۀ زير يک خطاي زمان اجرا دارد؛ به بخشي از حافظه دستيابي ميکند که از محدودۀ آرايه بيرون است:in main(){ const int SIZE=4; float a[SIZE} = { 33.3, 44.4, 55.5, 66.6 }; for (int i=0; i<7; i++) //ERROR: index is out of bounds! cout << ta[ << i << ] = << a[i] << endl;} a[0] = 33.3a[1] = 44.4a[2] = 55.5a[3] = 66.6a[4] = 5.60519e-45a[5] = 6.01888e-39a[6] = 6.01889e-39آرايهاي که در اين برنامه تعريف شده، چهار عنصر دارد ولي تلاش ميشود به هفت عنصر دستيابي شود. سه مقدار آخر واقعا جزو آرايه نيستند و فقط سلولهايي از حافظهاند که دقيقا بعد از عنصر چهارم آرايه قرار گرفتهاند. اين سلولها داراي مقدار زباله هستند.
اسلاید 250: * مثال 7-6 اثر همسايگيبرنامۀ زير از ايندکس خارج از محدوده استفاده ميکند و اين باعث ميشود که مقدار يک متغير به طور ناخواسته تغيير کند:int main(){ const int SIZE=4; float a[] = { 22.2, 44.4, 66.6 }; float x=11.1; cout << x = << x << endl; a[3] = 88.8; // ERROR: index is out of bounds! cout << x = << x << endl;}x = 88.8
اسلاید 251: متغير x بعد از آرايۀ a اعلان شده، پس يک سلول چهاربايتي بلافاصله بعد از دوازده بايت آرايه به آن تخصيص مييابد. بنابراين وقتي برنامه تلاش ميکند مقدار 88.8 را در a[3] قرار دهد (که جزو آرايه نيست) اين مقدار به شکل ناخواسته در x قرار ميگيرد. شکل مقابل نشان ميدهد چطور اين اتفاق در حافظه رخ ميدهد.a 0 22.21 44.42 66.6x 88.822.244.466.688.8اين خطا يکي از وحشتناکترين خطاهاي زمان اجراست زيرا ممکن است اصلا نتوانيم منبع خطا را کشف کنيم. حتي ممکن است به اين روش دادههاي برنامههاي ديگري که در حال کارند را خراب کنيم و اين باعث ايجاد اختلال در کل سيستم شود. به اين خطا «اثر همسايگي» ميگويند. اين وظيفۀ برنامهنويس است که تضمين کند ايندکس آرايه هيچگاه از محدودۀ آن خارج نشود.مثال بعدي نوع ديگري از خطاي زمان اجرا را نشان ميدهد: وقتي ايندکس آرايه بيش از حد بزرگ باشد.
اسلاید 252: مثال 8-6 ايجاد استثناي مديريت نشدهبرنامۀ زير از كار ميافتد زيرا ايندكس آرايه خيلي بزرگ است:int main(){ const int SIZE=4; float a[] = { 22.2, 44.4, 66.6 }; float x=11.1; cout << x = << x << endl; a[3333] =88.8;//ERROR: index is out of bounds! cout << x = << x << endl;}
اسلاید 253: وقتي اين برنامه روي رايانهاي با سيستم عامل ويندوز اجرا شود، يک صفحۀ هشدار که در شکل نشان داده شده روي صفحه ظاهر ميشود. اين پنجره بيان ميکند که برنامه تلاش دارد به نشاني 0040108e از حافظه دستيابي کند. اين مکان خارج از حافظۀ تخصيصي است که براي اين برنامه منظور شده، بنابراين سيستم عامل برنامه را متوقف ميکند.
اسلاید 254: خطايي که در مثال 8-6 بيان شده يک «استثناي مديريت نشده» ناميده ميشود زيرا کدي وجود ندارد که به اين استثنا پاسخ دهد. در C++ ميتوانيم کدهايي به برنامه اضافه کنيم که هنگام رخ دادن حالتهاي استثنا، از توقف برنامه جلوگيري کند. به اين کدها «پردازشگر استثنا» ميگويند. پردازشگر استثنا
اسلاید 255: 5- ارسال آرايه به تابعكد float a[]; كه آرايه a را اعلان ميكند دو چيز را به كامپايلر ميگويد:1- اين که نام آرايه a است 2- عناصر آرايه از نوع float هستند. سمبل a نشاني حافظۀ آرايه را ذخيره ميکند. لازم نيست تعداد عناصر آرايه به کامپايلر گفته شود زيرا از روي نشاني موجود در a ميتوان عناصر را بازيابي نمود. به همين طريق ميتوان يک آرايه را به تابع ارسال کرد. يعني فقط نوع آرايه و نشاني حافظۀ آن به عنوان پارامتر به تابع فرستاده ميشود.
اسلاید 256: مثال 9-6 ارسال آرايه به تابعي كه مجموع عناصر آرايه را برميگرداندint sum(int[],int);int main(){ int a[] = { 11, 33, 55, 77 }; int size = sizeof(a)/sizeof(int); cout << sum(a,size) = << sum(a,size) << endl;}int sum(int a[], int n){ int sum=0; for (int i=0; i<n; i++) sum += a[i]; return sum;}فهرست پارامتر تابع فوق به شکل (int a[], int n) است به اين معنا که اين تابع يک آرايه از نوع int و يک متغير از نوع int دريافت ميکند. به اعلان اين تابع در بالاي تابع main() نگاه کنيد. نام پارامترها حذف شده است.
اسلاید 257: هنگام فراخواني تابع نيز از عبارت sum(a,size) استفاده شده که فقط نام آرايه به تابع ارسال شده. نام آرايه در حقيقت نشاني اولين عنصر آرايه است (يعني a[0])تابع از اين نشاني براي دستيابي به عناصر آرايه استفاده ميکند. همچنين تابع ميتواند با استفاده از اين نشاني، محتويات عناصر آرايه را دستکاري کند. پس ارسال آرايه به تابع شبيه ارسال متغير به طريق ارجاع است. به مثال بعدي دقت کنيد.
اسلاید 258: مثال 10-6 توابع ورودي و خروجي براي يک آرايه در اين برنامه از تابع read() استفاده ميشود تا مقاديري به داخل آرايه وارد شود. سپس با استفاده از تابع print() مقادير داخل آرايه چاپ ميشوند:void read(int[],int&;)void print(int[],int);int main(){ const int MAXSIZE=100; int a[MAXSIZE]={0}, size; read(a,size); cout << The array has << size << elements: ; print(a,size);}Enter integers. Terminate with 0:a[0]: 11a[1]: 22a[2]: 33a[3]: 44a[4]: 0The array has 4 elements: 11 22 33 44
اسلاید 259: void read(int a[], int& n){ cout << Enter integers. Terminate with 0:n; n = 0; do { cout << a[ << n << ]: ; cin >> a[n]; { while (a[n++] !=0 && n < MAXSIZE); --n; // dont count the 0}
اسلاید 260: void print(int a[], int n){ for (int i=0; i<n; i++) cout << a[i] << ;}چون n يك متغير است، براي اين که تابع read() بتواند مقدار آن را تغيير دهد اين متغير بايد به شکل ارجاع ارسال شود. همچنين براي اين که تابع مذکور بتواند مقادير داخل آرايه a را تغيير دهد، آرايه نيز بايد به طريق ارجاع ارسال شود، اما ارجاع آرايهها کمي متفاوت است.
اسلاید 261: در C++ توابع قادر نيستند تعداد عناصر آرايۀ ارسالي را تشخيص دهند. بنابراين به منظور ارسال آرايهها به تابع از سه مشخصه استفاده ميشود: 1 – آدرس اولين خانۀ آرايه 2 – تعداد عناصر آرايه 3 – نوع عناصر آرايه تابع با استفاده از اين سه عنصر ميتواند به تک تک اعضاي آرايه دستيابي کند.
اسلاید 262: آدرس اولين خانۀ آرايه، همان نام آرايه است. پس وقتي نام آرايه را به تابع بفرستيم آدرس اولين خانه را به تابع فرستادهايم. نوع آرايه نيز در تعريف تابع اعلان ميشود. بنابراين با اين دو مقدار، تابع ميتواند به آرايه دسترسي داشته باشد.
اسلاید 263: برنامۀ زير، آدرس ذخيره شده در نام آرايه و مقدار موجود در آن خانه را چاپ ميکند:int main(){ int a[] = { 22, 44, 66, 88 }; cout << a = << a << endl; // the address of a[0] cout << a[0] = << a[0]; // the value of a[0]}مثال 11-6 آدرس اولين خانۀ آرايه و مقدار درون آنa = 0x0064fdeca[0] = 22اين برنامه تلاش ميکند که به طور مستقيم مقدار a را چاپ کند. نتيجۀ چاپ a اين است که يک آدرس به شکل شانزده دهي چاپ ميشود. اين همان آدرس اولين خانۀ آرايه است. يعني درون نام a آدرس اولين عنصر آرايه قرار گرفته. خروجي نيز نشان ميدهد که a آدرس اولين عنصر را دارد و a[0] مقدار اولين عنصر را.
اسلاید 264: 6- الگوريتم جستجوي خطيآرايهها بيشتر براي پردازش يک زنجيره از دادهها به کار ميروند. اغلب لازم است که بررسي شود آيا يک مقدار خاص درون يک آرايه موجود است يا خير. سادهترين راه اين است که از اولين عنصر آرايه شروع کنيم و يکي يکي همۀ عناصر آرايه را جستجو نماييم تا بفهميم که مقدار مورد نظر در کدام عنصر قرار گرفته. به اين روش «جستجوي خطي» ميگويند.
اسلاید 265: int index(int,int[],int);int main(){ int a[] = { 22, 44, 66, 88, 44, 66, 55}; cout << index(44,a,7) = << index(44,a,7) << endl; cout << index(50,a,7) = << index(50,a,7) << endl;}int index(int x, int a[], int n){ for (int i=0; i<n; i++) if (a[i] == x) return i; return n; // x not found}مثال 12-6 جستجوي خطيبرنامۀ زير تابعي را آزمايش ميکند که در اين تابع از روش جستجوي خطي براي يافتن يک مقدار خاص استفاده شده:index(44,a,7) = 1index(40,a,7) = 7
اسلاید 266: تابع index() سه پارامتر دارد: پارامتر x مقداري است که قرار است جستجو شود، پارامتر a آرايهاي است که بايد در آن جستجو صورت گيرد و پارامتر n هم ايندکس عنصري است که مقدار مورد نظر در آن پيدا شده است. در اين تابع با استفاده از حلقۀ for عناصر آرايه a پيمايش شده و مقدار هر عنصر با x مقايسه ميشود. اگر اين مقدار با x برابر باشد، ايندکس آن عنصر بازگردانده شده و تابع خاتمه مييابد.
اسلاید 267: اگر مقدار x در هيچ يک از عناصر آرايه موجود نباشد، مقداري خارج از ايندکس آرايه بازگردانده ميشود که به اين معناست که مقدار x در آرايۀ a موجود نيست. در اولين اجراي آزمايشي، مشخص شده که مقدار 44 در a[1] واقع است و در اجراي آزمايشي دوم مشخص شده که مقدار 40 در آرايۀ a موجود نيست (يعني مقدار 44 در a[7] واقع است و از آنجا که آرايۀ a فقط تا a[6] عنصر دارد، مقدار 7 نشان ميدهد که 40 در آرايه موجود نيست).
اسلاید 268: 7- مرتبسازي حبابي «مرتبسازي حبابي» يکي از سادهترين الگوريتمهاي مرتبسازي است. در اين روش، آرايه چندين مرتبه پويش ميشود و در هر مرتبه بزرگترين عنصر موجود به سمت بالا هدايت ميشود و سپس محدودۀ مرتبسازي براي مرتبۀ بعدي يکي کاسته ميشود. در پايان همۀ پويشها، آرايه مرتب شده است.
اسلاید 269: طريقۀ يافتن بزرگترين عنصر و انتقال آن به بالاي عناصر ديگر به اين شکل استاولين عنصر آرايه با عنصر دوم مقايسه ميشود. اگر عنصر اول بزرگتر بود، جاي اين دو با هم عوض ميشود. سپس عنصر دوم با عنصر سوم مقايسه ميشود. اگر عنصر دوم بزرگتر بود، جاي اين دو با هم عوض ميشود و به همين ترتيب مقايسه و جابجايي زوجهاي همسايه ادامه مييابد تا وقتي به انتهاي آرايه رسيديم، بزرگترين عضو آرايه در خانۀ انتهايي قرار خواهد گرفت.در اين حالت محدودۀ جستجو يکي کاسته ميشود و دوباره زوجهاي کناري يکي يکي مقايسه ميشوند تا عدد بزرگتر بعدي به مکان بالاي محدوده منتقل شود. اين پويش ادامه مييابد تا اين که وقتي محدوده جستجو به عنصر اول محدود شد، آرايه مرتب شده است.
اسلاید 270: مثال 13-6 مرتبسازيبرنامۀ زير تابعي را آزمايش ميکند که اين تابع با استفاده از مرتبسازي حبابي يک آرايه را مرتب مينمايد: void print(float[],int);void sort(float[],int);int main(){float a[]={55.5,22.2,99.9,66.6,44.4,88.8,33.3, 77.7}; print(a,8); sort(a,8); print(a,8);}55.5, 22.2, 99.9, 66.6, 44.4, 88.8, 33.3, 77.722.2, 33.3, 44.4, 55.5, 66.6, 77.7, 88.8, 99.9
اسلاید 271: void sort(float a[], int n){ // bubble sort: for (int i=1; i<n; i++) // bubble up max{a[0..n-i]}: for (int j=0; j<n-i; j++) if (a[j] > a[j+1]) swap (a[j],a[j+1]); //INVARIANT: a[n-1-i..n-1] is sorted}
اسلاید 272: تابع sort() از دو حلقۀ تودرتو استفاده ميكند.1- حلقه for داخلي زوجهاي همسايه را با هم مقايسه ميكند و اگر آنها خارج از ترتيب باشند، جاي آن دو را با هم عوض ميکند. وقتي for داخلي به پايان رسيد، بزرگترين عنصر موجود در محدودۀ فعلي به انتهاي آن هدايت شده است. 2-سپس حلقۀ for بيروني محدودۀ جستجو را يکي کم ميکند و دوباره for داخلي را راه مياندازد تا بزرگترين عنصر بعدي به سمت بالاي آرايه هدايت شود.
اسلاید 273: 8- الگوريتم جستجوي دودوييدر روش جستجوي دودويي به يک آرايۀ مرتب نياز است. هنگام جستجو آرايه از وسط به دو بخش بالايي و پاييني تقسيم ميشود. مقدار مورد جستجو با آخرين عنصر بخش پاييني مقايسه ميشود. اگر اين عنصر کوچکتر از مقدار جستجو بود، مورد جستجو در بخش پاييني وجود ندارد و بايد در بخش بالايي به دنبال آن گشت.
اسلاید 274: دوباره بخش بالايي به دو بخش تقسيم ميگردد و گامهاي بالا تکرار ميشود. سرانجام محدودۀ جستجو به يک عنصر محدود ميشود که يا آن عنصر با مورد جستجو برابر است و عنصر مذکور يافت شده و يا اين که آن عنصر با مورد جستجو برابر نيست و لذا مورد جستجو در آرايه وجود ندارد. اين روش پيچيدهتر از روش جستجوي خطي است اما در عوض بسيار سريعتر به جواب ميرسيم.
اسلاید 275: مثال 14-6 جستجوي دودوييبرنامۀ آزمون زير با برنامۀ آزمون مثال 12-6 يکي است اما تابعي که در زير آمده از روش جستجوي دودويي براي يافتن مقدار درون آرايه استفاده ميکند: int index(int, int[],int);int main(){ int a[] = { 22, 33, 44, 55, 66, 77, 88 }; cout << index(44,a,7) = << index(44,a,7) << endl; cout << index(60,a,7) = << index(60,a,7) << endl;}
اسلاید 276: int index(int x, int a[], int n){ // PRECONDITION: a[0] <= a[1] <= ... <= a[n-1]; // binary search: int lo=0, hi=n-1, i; while (lo <= hi) { i = (lo + hi)/2; // the average of lo and hi if (a[i] == x) return i; if (a[i] < x) lo = i+1; // continue search in a[i+1..hi] else hi = i-1; // continue search in a[0..i-1] } return n; // x was not found in a[0..n-1]}index(44,a,7) = 2index(60,a,7) = 7
اسلاید 277: براي اين که بفهميم تابع چطور کار ميکند، فراخواني index(44,a,7) را دنبال ميکنيم. وقتي حلقه شروع ميشود، x=44 و n=7 و lo=0 و hi=6 است. ابتدا i مقدار (0+6)/2 = 3 را ميگيرد.پس عنصر a[i] عنصر وسط آرايۀ a[0..6] است. مقدار a[3] برابر با 55 است که از مقدار x بزرگتر است. پس x در نيمۀ بالايي نيست و جستجو در نيمۀ پاييني ادامه مييابد. لذا hi با i-1 يعني 2 مقداردهي ميشود و حلقه تکرار ميگردد.
اسلاید 278: حالا hi=2 و lo=0 است و دوباره عنصر وسط آرايۀ a[0..2] يعني a[1] با x مقايسه ميشود. a[1] برابر با 33 است که کوچکتر از x ميباشد. پس اين دفعه lo برابر با i+1 يعني 2 ميشود. در سومين دور حلقه، hi=2 و lo=2 است. پس عنصر وسط آرايۀ a[2..2] که همان a[2] است با x مقايسه ميشود. a[2] برابر با 44 است که با x برابر است. پس مقدار 2 بازگشت داده ميشود؛ يعني x مورد نظر در a[2] وجود دارد.
اسلاید 279: lohiia[i]??x06355>442133<442244==44
اسلاید 280: حال فراخواني index(60,a,7) را دنبال ميکنيم. وقتي حلقه شروع ميشود، x=60 و n=7 و lo=0 و hi=6 است. عنصر وسط آرايۀ a[0..6] عنصر a[3]=55 است که از x کوچکتر است. پس lo برابر با i+1=4 ميشود و حلقه دوباره تکرار ميشود. اين دفعه hi=6 و lo=4 است . عنصر وسط آرايۀ a[4..6] عنصر a[5]=77 است که بزرگتر از x ميباشد. پس hi به i-1=4 تغيير مييابد و دوباره حلقه تکرار ميشود. اين بار hi=4 و lo=4 است و عنصر وسط آرايۀ a[4..4] عنصر a[4]=66 است که بزرگتر از x ميباشد. لذا hi به i-1=3 کاهش مييابد.
اسلاید 281: lohiia[i]??x06355<604577>604466>60اکنون شرط حلقه غلط ميشود زيرا hi<lo است. بنابراين تابع مقدار 7 را برميگرداند يعني عنصر مورد نظر در آرايه موجود نيست.
اسلاید 282: در تابع فوق هر بار که حلقه تکرار ميشود، محدودۀ جستجو 50% کوچکتر ميشود. در آرايۀ n عنصري، روش جستجوي دودويي حداکثر به مقايسه نياز دارد تا به پاسخ برسد. حال آن که در روش جستجوي خطي به n مقايسه نياز است.
اسلاید 283: تفاوتهاي جستجوي دودويي و خطي جستجوي دودويي سريعتر از جستجوي خطي است. دومين تفاوت در اين است که اگر چند عنصر داراي مقادير يکساني باشند، آنگاه جستجوي خطي هميشه کوچکترين ايندکس را برميگرداند ولي در مورد جستجوي دودويي نميتوان گفت که کدام ايندکس بازگردانده ميشود. سومين فرق در اين است که جستجوي دودويي فقط روي آرايههاي مرتب کارايي دارد و اگر آرايهاي مرتب نباشد، جستجوي دودويي پاسخ غلط ميدهد ولي جستجوي خطي هميشه پاسخ صحيح خواهد داد.
اسلاید 284: * مثال 15-6 مشخص كردن اين كه آيا آرايه مرتب است يا خيربرنامۀ زير يک تابع بولي را آزمايش ميکند. اين تابع مشخص مينمايد که آيا آرايۀ داده شده غير نزولي است يا خير: bool isNondecreasing(int a[], int n);int main(){ int a[] = { 22, 44, 66, 88, 44, 66, 55 }; cout<<isNondecreasing(a,4) = << isNondecreasing(a,4)<< endl; cout<<isNondecreasing(a,7) = << isNondecreasing(a,7) << endl;}
اسلاید 285: bool isNondecreasing(int a[], int n){ // returns true iff a[0] <= a[1] <= ... <= a[n-1]: for (int i=1; i<n; i++) if (a[i]<a[i-1]) return false; return true;}isNondecreasing(a,4) = 1isNondecreasing(a,7) = 0
اسلاید 286: اين تابع يک بار کل آرايه را پيمايش کرده و زوجهاي a[i-1] و a[i] را مقايسه ميکند. اگر زوجي يافت شود که در آن a[i]<a[i-1] باشد، مقدار false را بر ميگرداند به اين معني که آرايه مرتب نيست. ببينيد که مقادير true و false به شکل اعداد 1 و 0 در خروجي چاپ ميشوند زيرا مقادير بولي در حقيقت به شکل اعداد صحيح در حافظه ذخيره ميشوند.
اسلاید 287: اگر پيششرط مثال 14-6 يعني مرتب بودن آرايه رعايت نشود، جستجوي دودويي پاسخ درستي نميدهد. به اين منظور ابتدا بايد اين پيششرط بررسي شود. با استفاده از تابع assert() ميتوان اجراي يک برنامه را به يک شرط وابسته کرد. اين تابع يک آرگومان بولي ميپذيرد. اگر مقدار آرگومان false باشد، برنامه را خاتمه داده و موضوع را به سيستم عامل گزارش ميکند. اگر مقدار آرگومان true باشد، برنامه بدون تغيير ادامه مييابد. تابع asset() در سرفايل <cassert> تعريف شده است.
اسلاید 288: مثال 16-6 استفاده از تابع assert() براي رعايت كردن يك پيششرطبرنامۀ زير نسخۀ بهبوديافتهاي از تابع search() مثال 14-6 را آزمايش ميکند. در اين نسخه، از تابع isNonDecreasing() مثال 15-6 استفاده شده تا مشخص شود آرايه مرتب است يا خير. نتيجه اين تابع به تابع assert() ارسال ميگردد تا اگر آرايه مرتب نباشد برنامه به بيراهه نرود.
اسلاید 289: #include <cassert> // defines the assert() function#include <iostream> // defines the cout objectusing namespace std;int index(int x, int a[], int n);int main(){ int a[] = { 22, 33, 44, 55, 66, 77, 88, 60 }; cout<<index(44,a,7) = << index(44,a,7) << endl; cout<<index(44,a,8) = << index(44,a,8) << endl; cout<<index(60,a,8) = << index(60,a,8) << endl;}
اسلاید 290: bool isNondecreasing(int a[], int n);int index(int x, int a[], int n){ assert(isNondecreasing(a,n)); int lo=0, hi=n-1, i; while (lo <= hi) { i = (lo + hi)/2; if (a[i] == x) return i; if (a[i] < x) lo = i+1; else hi = i-1; } return n; }index(44,a,7) = 2
اسلاید 291: آرايۀ a[] که در اين برنامه استفاده شده كاملا مرتب نيست اما هفت عنصر اول آن مرتب است. بنابراين در فراخوانيindex(44,a,7) تابع بولي مقدار true را به assert() ارسال ميکند و برنامه ادمه مييابد. اما در دومين فراخواني index(44,a,8) باعث ميشود که تابع isNondecreasing() مقدار false را به تابع assert() ارسال کند كه در اين صورت برنامه متوقف ميشود و ويندوز پنجرۀ هشدار مقابل را نمايش ميدهد.
اسلاید 292: 9- استفاده از انواع شمارشي در آرايهانواع شمارشي در جلسه دوم توضيح داده شدهاند. با استفاده از انواع شمارشي نيز ميتوان آرايهها را پردازش نمود. مثال 17-7 شمارش با استفاده از روزهاي هفتهاين برنامه يك آرايه به نام high[] با هفت عنصرازنوعfloat تعريف ميكند كه هر عنصر حداکثر دما در يک روز هفته را نشان ميدهد:int main(){ enum Day { SUN, MON, TUE, WED, THU, FRI, SAT }; float high[SAT+1] = {28.6, 29.1, 29.9, 31.3, 30.4, 32.0, 30.7}; for (int day = SUN; day <= SAT; day++) cout << The high temperature for day << day << was << high[day] << endl;}The high temperature for day 0 was 28.6The high temperature for day 1 was 29.1The high temperature for day 2 was 29.9The high temperature for day 3 was 31.3The high temperature for day 4 was 30.4The high temperature for day 5 was 32.0The high temperature for day 6 was 30.7
اسلاید 293: به خاطر بياوريد که انواع شمارشي به شکل مقادير عددي ذخيره ميشوند. اندازۀ آرايه، SAT+1 است زيرا SAT مقدار صحيح 6 را دارد و آرايه به هفت عنصر نيازمند است. متغير day از نوع int است پس ميتوان مقادير Day را به آن تخصيص داد. استفاده از انواع شمارشي در برخي از برنامهها باعث ميشود که کد برنامه «خود استناد» شود. مثلا در مثال 17-6 کنترل حلقه به شکل for (int day = SUN; day <= SAT; day++)باعث ميشود که هر بينندهاي حلقۀ for بالا را به خوبي درک کند.
اسلاید 294: 10- تعريف انواعانواع شمارشي يكي از راههايي است که کاربر ميتواند نوع ساخت خودش را تعريف کند. براي مثال دستور زير :enum Color{ RED,ORANGE,YELLOW, GREEN, BLUE, VIOLET };يک نوع جديد به نام Color تعريف ميکند که متغيرهايي از اين نوع ميتوانند مقادير RED يا ORANGE يا YELLOW يا GREEN يا BLUE يا VIOLET را داشته باشند. پس با استفاده از اين نوع ميتوان متغيرهايي به شکل زير تعريف نمود:Color shirt = BLUE;Color car[] = { GREEN, RED, BLUE, RED };Floatwavelength[VIOLET+1]={420,480,530,570,600,620}; در اينجا shirt متغيري از نوع Color است و با مقدار BLUE مقداردهي شده. car يک آرايۀ چهار عنصري است و مقدار عناصر آن به ترتيب GREEN و RED و BLUE و RED ميباشد. همچنين wavelength آرايهاي از نوع float است که داراي VIOLET+1 عنصر يعني 5+1=6 عنصر است.
اسلاید 295: در C++ ميتوان نام انواع استاندارد را تغيير داد. کلمۀ کليدي typedef يک نام مستعار براي يک نوع استاندارد موجود تعريف ميکند. نحو استفاده از آن به شکل زير است:typedef type alias;كه type يک نوع استاندارد و alias نام مستعار براي آن است.
اسلاید 296: براي مثال کساني که با پاسکال برنامه مينويسند به جاي نوع long از عبارت Integer استفاده ميکنند و به جاي نوع double از عبارت Real استفاده مينمايند. اين افراد ميتوانند به شکل زير از نام مستعار استفاده کنند:typedef long Integer;typedef double Real;و پس از آن کدهاي زير معتبر خواهند بود:Integer n = 22;const Real PI = 3.141592653589793;Integer frequency[64];اگر دستور typedef را به شکل زير بکار ببريم ميتوانيم آرايهها را بدون علامت براکت تعريف کنيم:typedef element-type alias[];مثل تعريف زير :typedef float sequence[];سپس ميتوانيم آرايۀ a را به شکل زير اعلان کنيم:sequence a = {55.5, 22.2, 99.9};
اسلاید 297: دستور typedef نوع جديدي را اعلان نميکند، بلکه فقط به يک نوع موجود نام مستعاري را نسبت ميدهد. مثال بعدي نحوۀ به کارگيري typedef را نشان ميدهد. برنامۀ زير همان برنامۀ مثال 13-6 است با اين فرق که از typedef استفاده شده تا بتوان از نام مستعار sequrnce به عنوان يک نوع استفاده کرد. سپس اين نوع در فهرست پارامترها و اعلان a در تابع main() به کار رفته است:
اسلاید 298: typedef float Sequence[];void sort(Sequence,int);void print(Sequence,int);int main(){ Sequence a = {55.5, 22.2, 99.9, 66.6, 44.4, 88.8, 33.3, 77.7}; print(a,8); sort(a,8); print(a,8);}
اسلاید 299: void sort(Sequence a, int n){ for (int i=n-1; i>0; i--) for (int j=0; j<i; j++) if (a[j] > a[j+1]) swap(a[j],a[j+1]);}دوباره به دستور typedef نگاه کنيد:typedef float Seguence[];علامت براكتها [] نشان ميدهند که هر چيزي که از نوع Sequence تعريف شود، يک آرايه است و عبارت float نيز بيان ميکند که اين آرايه از نوع float است.
اسلاید 300: 11- آرايههاي چند بعديهمۀ آرايههايي كه تاکنون تعريف کرديم، يک بعدي هستند، خطي هستند، رشتهاي هستند. ميتوانيم آرايهاي تعريف کنيم که از نوع آرايه باشد، يعني هر خانه از آن آرايه، خود يک آرايه باشد. به اين قبيل آرايهها، آرايههاي چندبعدي ميگوييم. يک آرايۀ دو بعدي آرايهاي است که هر خانه از آن، خود يک آرايۀ يک بعدي باشد. يک آرايۀ سه بعدي آرايهاي است که هر خانه از آن يک آرايۀ دو بعدي باشد.
اسلاید 301: دستور int a[5]; آرايهاي با پنج عنصر از نوع int تعريف ميکند. اين يک آرايۀ يک بعدي است. دستور int a[3][5]; آرايهاي با سه عنصر تعريف ميکند که هر عنصر، خود يک آرايۀ پنج عنصري از نوع int است. اين يک آرايۀ دو بعدي است که در مجموع پانزده عضو دارد. دستور int a[2][3][5]; آرايهاي با دو عنصر تعريف ميکند که هر عنصر، سه آرايه است که هر آرايه پنج عضو از نوع int دارد. اين يک آرايۀ سه بعدي است که در مجموع سي عضو دارد. شکل دستيابي به عناصر در آرايههاي چند بعدي مانند آرايههاي يک بعدي است. مثلا دستور a[1][2][3] = 99;مقدار 99 را در عنصري قرار ميدهد که ايندکس آن عنصر(1,2,3) است. آرايههاي چند بعدي مثل آرايههاي يک بعدي به توابع فرستاده ميشوند با اين تفاوت که هنگام اعلان و تعريف تابع مربوطه، بايد تعداد عناصر بُعد دوم تا بُعد آخر حتما ذکر شود.
اسلاید 302: مثال 19-6 نوشتن و خواندن يك آرايۀ دو بعديبرنامۀ زير نشان ميدهد که يک آرايۀ دوبعدي چگونه پردازش ميشود: void read(int a[][5]);void print(int a[][5]);int main(){ int a[3][5]; read(a); print(a);}
اسلاید 303: void read(int a[][5]){ cout << Enter 15 integers, 5 per row:n; for (int i=0; i<3; i++) { cout << ROW << i << : ; for (int j=0; j<5; j++) cin >> a[i][j]; }
اسلاید 304: void print(const int a[][5]){ for (int i=0; i<3; i++) { for (int j=0; j<5; j++) cout << << a[i][j]; cout << endl; }}
اسلاید 305: Enter 15 integers, 5 per row: row 0: 44 77 33 11 44 row 1: 60 50 30 90 70 row 2: 65 25 45 45 55 44 77 33 11 44 60 50 30 90 70 65 25 45 45 55دقت کنيد که در فهرست پارامترهاي توابع بالا، بعد اول نامشخص است اما بعد دوم مشخص شده. علت هم اين است که آرايۀ دو بعدي a[][] در حقيقت آرايهاي يکبعدي از سه آرايۀ پنج عنصري است. کامپايلر نياز ندارد بداند که چه تعداد از اين آرايههاي پنج عنصري موجود است، اما بايد بداند که آنها پنج عنصري هستند.
اسلاید 306: وقتي يک آرايۀ چند بعدي به تابع ارسال ميشود، بُعد اول مشخص نيست اما همۀ ابعاد ديگر بايد مشخص باشند. مثال 20-6 پردازش يك آرايۀ دوبعدي از نمرات امتحاني const NUM_STUDENTS = 3;const NUM_QUIZZES = 5;typedef int Score[NUM_STUDENTS][NUM_QUIZZES];void read(Score);void printQuizAverages(Score);void printClassAverages(Score);
اسلاید 307: int main(){ Score score; cout << Enter << NUM_QUIZZES << quiz scores for each student:n; read(score); cout << The quiz averages are:n; printQuizAverages(score); cout << The class averages are:n; printClassAverages(score);}
اسلاید 308: void read(Score score){ for (int s=0; s<NUM_STUDENTS; s++) { cout << Student << s << : ; for(int q=0; q<NUM_QUIZZES; q++) cin >> score[s][q]; }}
اسلاید 309: void printQuizAverages(Score score){ for (int s=0; s<NUM_STUDENTS; s++) { float sum = 0.0; for (int q=0; q<NUM_QUIZZES; q++) sum += score[s][q]; cout << tStudent << s << : << sum/NUM_QUIZZES << endl; }}
اسلاید 310: void printClassAverages(Score score){ for (int q=0; q<NUM_QUIZZES; q++) { float sum = 0.0; for (int s=0; s<NUM_STUDENTS; s++) sum += score[s][q]; cout << tQuiz << q << : << sum/NUM_STUDENTS << endl; }}
اسلاید 311: Enter 5 quiz scores for each student:student 0: 8 7 9 8 9student 1: 9 9 9 9 8student 2: 5 6 7 8 9The quize averages are: student 0: 8.2 student 1: 8.8 student 2: 7The class averages are: Quiz 0: 7.33333 Quiz 1: 7.33333 Quiz 2: 8.33333 Quiz 3: 8.33333 Quiz 4: 8.66667در برنامۀ فوق با استفاده از دستور typedef براي آرايههاي دوبعدي 3*5 نام مستعار Score انتخاب شده. اين باعث ميشود که توابع خواناتر باشند. هر تابع از دو حلقۀ for تودرتو استفاده کرده که حلقۀ بيروني، بعد اول را پيمايش ميکند و حلقۀ دروني بعد دوم را پيمايش مي نمايد.تابع printQuizAverages() ميانگين هر سطر از نمرات را محاسبه و چاپ مينمايد و تابع printClassAverages() ميانگين هر ستون از نمرهها را چاپ ميكند.
اسلاید 312: مثال 21-6 پردازش يك آرايۀ سه بعدياين برنامه تعداد صفرها را در يك آرايۀ سه بعدي ميشمارد:int numZeros(int a[][4][3], int n1, int n2, int n3);int main(){ int a[2][4][3]={{{5,0,2}, {0,0,9},{4,1,0},{7,7,7} }, { {3,0,0}, {8,5,0}, {0,0,0}, {2,0,9} } }; cout << This array has << numZeros(a,2,4,3) << zeros:n;}
اسلاید 313: int numZeros(int a[][4][3], int n1, int n2, int n3){ int count = 0; for (int i = 0; i < n1; i++) for (int j = 0; j < n2; j++) for (int k = 0; k < n3; k++) if (a[i][j][k] == 0) ++count; return count;}This array has 11 zeros:
اسلاید 314: توجه كنيد كه آرايه چگونه مقداردهي شده است. اين قالب مقداردهي به خوبي نمايان ميکند که آرايۀ مذکور يک آرايه دو عنصري است که هر عنصر، خود يک آرايۀ چهار عضوي است که هر عضو شامل آرايهاي سه عنصري ميباشد. پس اين آرايه در مجموع 24 عنصر دارد. آرايۀ مذکور را به شکل زير نيز ميتوانيم مقداردهي کنيم:int a[2][4][3]={5,0,2,0,0,9,4,1,0,7,7,7,3,0,0,8,5,0,0,0,0,2,0,9};و يا مانند اين:int a[2][4][3] = {{5,0,2,0,0,9,4,1,0,7,7,7},{3,0,0,8,5,0,0,0,0,2,0,9}};هر سۀ اين قالبها براي کامپايلر يک مفهوم را دارند اما با نگاه کردن به دو قالب اخير به سختي ميتوان فهميد که کدام عنصر از آرايه، کدام مقدار را خواهد داشت.
اسلاید 315: پايان جلسه ششم
اسلاید 316: جلسه هفتم«اشارهگرها و ارجاعها »
اسلاید 317: آنچه در اين جلسه مي خوانيد:1- عملگر ارجاع2- ارجاعها3- اشارهگرها4- مقداريابي5- چپ مقدارها، راست مقداره6- بازگشت از نوع ارجاع7- آرايهها و اشارهگرها›››
اسلاید 318: 8- عملگر new 9- عملگر delete 10- آرايههاي پويا 11- اشارهگر ثابت 12- آرايهاي از اشارهگرها 13- اشارهگري به اشارهگر ديگر 14- اشارهگر به توابع 15- NUL و NULL
اسلاید 319: هدف کلي: آشنايي با اشارهگرها و نحوۀ کار با آدرسهاي حافظههدفهاي رفتاري: انتظار ميرود پس از پايان اين جلسه بتوانيد:- «ارجاع» را تعريف کنيد و با استفاده از عملگر ارجاع به متغيرها دستيابي کنيد.- «اشارهگر» را بشناسيد و بتوانيد اشارهگرهايي به انواع مختلف ايجاد کرده و آنها را مقداريابي کنيد.»»»
اسلاید 320: - «چپمقدارها» و «راستمقدارها» را تعريف کرده و آنها را از يکديگر تميز دهيد.- بازگشتهايي از نوع ارجاع ايجاد نماييد.- طريقۀ استفاده از عملگرهاي new و delete و وظيفۀ هر يک را بدانيد.- «آرايههاي پويا» را تعريف کرده و مزيت آنها را نسبت به آرايههاي ايستا ذکر کنيد.- آرايههاي پويا را در برنامههايتان ايجاد کرده و مديريت نماييد.- تفاوت بين NUL و NULL را توضيح دهيد.
اسلاید 321: 1- مقدمه حافظۀ رايانه را ميتوان به صورت يک آرايۀ بزرگ در نظر گرفت. براي مثال رايانهاي با 256 مگابايت RAM در حقيقت حاوي آرايهاي به اندازۀ 268،435،456 (=228) خانه است که اندازۀ هر خانه يک بايت است.اين خانهها داراي ايندکس صفر تا 268،435،455 هستند. به ايندکس هر بايت، آدرس حافظۀ آن ميگويند.
اسلاید 322: آدرسهاي حافظه را با اعداد شانزدهدهي نشان ميدهند. پس رايانۀ مذکور داراي محدوده آدرس 0x00000000 تا 0x0fffffff ميباشد. هر وقت که متغيري را اعلان ميکنيم، سه ويژگي اساسي به آن متغير نسبت داده ميشود: «نوع متغير» و «نام متغير» و «آدرس حافظه» آن. مثلا اعلان int n; نوع int و نام n و آدرس چند خانه از حافظه که مقدار n در آن قرار ميگيرد را به يکديگر مرتبط ميسازد. فرض کنيد آدرس اين متغير 0x0050cdc0 است. بنابراين ميتوانيم n را مانند شکل مقابل مجسم کنيم:
اسلاید 323: 0x0050cdc0nintخود متغير به شکل جعبه نمايش داده شده. نام متغير، n، در بالاي جعبه است و آدرس متغير در سمت چپ جعبه و نوع متغير، int، در زير جعبه نشان داده شده. در بيشتر رايانهها نوع int چهار بايت از حافظه را اشغال مينمايد. بنابراين همان طور که در شکل مقابل نشان داده شده است، متغير n يک بلوک چهاربايتي از حافظه را اشغال ميکند که شامل بايتهاي 0x0050cdc0 تا 0x0050cdc3 است. توجه کنيد که آدرس شي، آدرس اولين بايت از بلوکي است که شي در آن جا ذخيره شده.
اسلاید 324: 0x0050cdb80x0050cdb90x0050cdc00x0050cdc10x0050cdc20x0050cdc30x0050cdc40x0050cdc5 32320x0050cdc0nintاگر متغير فوق به شکل int n=32; مقداردهي اوليه شود، آنگاه بلوک حافظه به شکل زير خواهد بود. مقدار 32 در چهار بايتي که براي آن متغير منظور شده ذخيره ميشود.
اسلاید 325: 2- عملگر ارجاعدر C++ براي بدست آوردن آدرس يک متغير ميتوان از عملگر ارجاع1 & استفاده نمود. به اين عملگر «علمگر آدرس» نيز ميگويند. عبارت &n آدرس متغير n را به دست ميدهد. int main(){ int n=44; cout << n = << n << endl; cout << &n = << &n << endl;}n = 44&n = 0x00c9fdc3
اسلاید 326: خروجي نشان ميدهد كه آدرس n در اين اجرا برابر با 0x00c9fdc3 است. ميتوان فهميد که اين مقدار بايد يک آدرس باشد زيرا به شکل شانزدهدهي نمايش داده شده. اعداد شانزدهدهي را از روي علامت 0x ميتوان تشخيص داد. معادل دهدهي عدد بالا مقدار 13,237,699 ميباشد.
اسلاید 327: 3- ارجاعهايك «ارجاع» يك اسم مستعار يا واژۀ مترادف براي متغير ديگر است. نحو اعلان يک ارجاع به شکل زير است:type& ref_name = var_name; type نوع متغير است، ref_name نام مستعار است و var_name نام متغيري است که ميخواهيم براي آن نام مستعار بسازيم. براي مثال در اعلان :int& rn=n; // r is a synonym for nrn يک ارجاع يا نام مستعار براي n است. البته n بايد قبلا اعلان شده باشد.
اسلاید 328: مثال 2-7 استفاده از ارجاعهادر برنامۀ زير rn به عنوان يک ارجاع به n اعلان ميشود:int main(){ int n=44; int& rn=n; // rn is a synonym for n cout << n = << n << , rn = << rn << endl; --n; cout << n = << n << , rn = << rn << endl; rn *= 2; cout << n = << n << , rn = << rn << endl;}n = 44, rn = 44n = 43, rn = 43n = 86, rn = 86 n و rn نامهاي متفاوتي براي يک متغير است. اين دو هميشه مقدار يکساني دارند. اگر n کاسته شود، rn نيز کاسته شده و اگر rn افزايش يابد، n نيز افزايش يافته است.
اسلاید 329: همانند ثابتها، ارجاعها بايد هنگام اعلان مقداردهي اوليه شوند با اين تفاوت که مقدار اوليۀ يک ارجاع، يک متغير است نه يک ليترال. بنابراين کد زير اشتباه است:int& rn=44; // ERROR: 44 is not a variable;گرچه برخي از کامپايلرها ممکن است دستور بالا را مجاز بدانند ولي با نشان دادن يک هشدار اعلام ميکنند که يک متغير موقتي ايجاد شده تا rn به حافظۀ آن متغير، ارجاع داشته باشد.
اسلاید 330: درست است که ارجاع با يک متغير مقداردهي ميشود، اما ارجاع به خودي خود يک متغير نيست. يک متغير، فضاي ذخيرهسازي و نشاني مستقل دارد، حال آن که ارجاع از فضاي ذخيرهسازي و نشاني متغير ديگري بهره ميبرد.
اسلاید 331: * مثال 3-7 ارجاعها متغيرهاي مستقل نيستندint main(){ int n=44; int& rn=n; // rn is a synonym for n cout << &n = << &n << , &rn = << &rn << endl; int& rn2=n; // rn2 is another synonym for n int& rn3=rn; // rn3 is another synonym for n cout << &rn2 = << &rn2 << , &rn3 = << &rn3 << endl;} &n = 0x0064fde4, &rn = 0x0064fde4&rn2 = 0x0064fde4, &rn3 = 0x0064fde4
اسلاید 332: در برنامۀ فوق فقط يک شي وجود دارد و آن هم n است. rn و rn2 و rn3 ارجاعهايي به n هستند. خروجي نيز تاييد ميکند که آدرس rn و rn2 و rn3 با آدرس n يکي است. يک شي ميتواند چند ارجاع داشته باشد. ارجاعها بيشتر براي ساختن پارامترهاي ارجاع در توابع به کار ميروند. تابع ميتواند مقدار يک آرگومان را که به طريق ارجاع ارسال شده تغيير دهد زيرا آرگومان اصلي و پارامتر ارجاع هر دو يک شي هستند. تنها فرق اين است که دامنۀ پارامتر ارجاع به همان تابع محدود شده است.
اسلاید 333: 4- اشارهگرهاميدانيم که اعداد صحيح را بايد در متغيري از نوع int نگهداري کنيم و اعداد اعشاري را در متغيرهايي از نوع float. به همين ترتيب کاراکترها را بايد در متغيرهايي از نوع char نگهداريم و مقدارهاي منطقي را در متغيرهايي از نوع bool. اما آدرس حافظه را در چه نوع متغيري بايد قرار دهيم؟
اسلاید 334: عملگر ارجاع & آدرس حافظۀ يک متغير موجود را به دست ميدهد. ميتوان اين آدرس را در متغير ديگري ذخيره نمود. متغيري که يک آدرس در آن ذخيره ميشود اشارهگر ناميده ميشود. براي اين که يک اشارهگر اعلان کنيم، ابتدا بايد مشخص کنيم که آدرس چه نوع دادهاي قرار است در آن ذخيره شود. سپس از عملگر اشاره * استفاده ميکنيم تا اشارهگر را اعلان کنيم.
اسلاید 335: براي مثال دستور :float* px;اشارهگري به نام px اعلان ميکند که اين اشارهگر، آدرس متغيرهايي از نوع float را نگهداري مينمايد. به طور کلي براي اعلان يک اشارهگر از نحو زير استفاده ميکنيم:type* pointername;که type نوع متغيرهايي است که اين اشارهگر آدرس آنها را نگهداري ميکند و pointername نام اشارهگر است. آدرس يک شي از نوع int را فقط ميتوان در اشارهگري از نوع int* ذخيره کرد و آدرس يک شي از نوع float را فقط ميتوان در اشارهگري از نوع float* ذخيره نمود. دقت کنيد که يک اشارهگر، يک متغير مستقل است.
اسلاید 336: * مثال 4-7 به کارگيري اشارهگرهابرنامۀ زير يک متغير از نوع int به نام n و يک اشارهگر از نوع int* به نام pn را اعلان ميکند:int main(){ int n=44; cout << n = << n << , &n = << &n << endl; int* pn=&n; // pn holds the address of n cout << pn = << pn << endl; cout << &pn = << &pn << endl;}n = 44, &n = 0x0064fddc pn = 0x0064fddc&pn = 0x0064fde0
اسلاید 337: متغير n با مقدار 44 مقداردهي شده و آدرس آن 0x0064fddc ميباشد. اشارهگر pn با مقدار &n يعني آدرس n مقداردهي شده. پس مقدار درون pn برابر با 0x0064fddc است (خط دوم خروجي اين موضوع را تاييد ميکند) . 44nint0x0064fddc0x0064fddcpnint*0x0064fde0
اسلاید 338: اما pn يک متغير مستقل است و آدرس مستقلي دارد. &pn آدرس pn را به دست ميدهد. خط سوم خروجي ثابت ميکند که متغير pn مستقل از متغير n است. تصوير زير به درک بهتر اين موضوع کمک ميکند. در اين تصوير ويژگيهاي مهم n و pn نشان داده شده. pn يک اشارهگر به n است و n مقدار 44 دارد.وقتي ميگوييم «pn به n اشاره ميکند» يعني درون pn آدرس n قرار دارد.44nintint*pnوقتي ميگوييم «pn به n اشاره ميکند» يعني درون pn آدرس n قرار دارد.
اسلاید 339: 5-مقداريابيفرض کنيد n داراي مقدار 22 باشد و pn اشارهگري به n باشد. با اين حساب بايد بتوان از طريق pn به مقدار 22 رسيد. با استفاده از * ميتوان مقداري که اشارهگر به آن اشاره دارد را به دست آورد. به اين کار مقداريابي اشارهگر ميگوييم.
اسلاید 340: مثال 5-7 مقداريابي يك اشارهگراين برنامه همان برنامۀ مثال 4-7 است. فقط يک خط کد بيشتر دارد:int main(){ int n=44; cout << n = << n << , &n = << &n << endl; int* pn=&n; // pn holds the address of n cout << pn = << pn << endl; cout << &pn = << &pn << endl; cout << *pn = << *pn << endl;}n = 44, &n = 0x0064fdcc pn = 0x0064fdcc&pn = 0x0064fdd0*pn = 44 ظاهرا *pn يک اسم مستعار براي n است زيرا هر دو يک مقدار دارند.
اسلاید 341: * مثال 6-7 اشارهگري به اشارهگرها اين کد ادامۀ ساختار برنامۀ مثال 4-7 است:int main(){ int n=44; cout << n = << n << endl; cout << &n = << &n << endl; int* pn=&n; // pn holds the address of n cout << pn = << pn << endl; cout << &pn = << &pn << endl; cout << *pn = << *pn << endl; int** ppn=&pn; // ppn holds the address of pn cout << ppn = << ppn << endl; cout << &ppn = << &ppn << endl; cout << *ppn = << *ppn << endl; cout << **ppn = << **ppn << endl;}يک اشارهگر به هر چيزي ميتواند اشاره کند، حتي به يک اشارهگر ديگر. به مثال زير دقت کنيد.
اسلاید 342: n = 44 &n = 0x0064fd78 pn = 0x0064fd78 &pn = 0x0064fd7c *pn = 44 ppn = 0x0064fd7c &ppn = 0x0064fd80 *ppn = 0x0064fd78**ppn = 4444nintint*pnint**ppnدر برنامۀ بالا متغير n از نوع int تعريف شده. pn اشارهگري است که به n اشاره دارد. پس نوع pn بايد int* باشد. ppn اشارهگري است که به pn اشاره ميکند. پس نوع ppn بايد int** باشد. همچنين چون ppn به pn اشاره دارد، پس *ppn مقدار pn را نشان ميدهد و چون pn به n اشاره دارد، پس *pn مقدار n را ميدهد.
اسلاید 343: عملگر مقداريابي * و عملگر ارجاع & معکوس يکديگر رفتار ميکنند. اگر اين دو را با هم ترکيب کنيم، يکديگر را خنثي مينمايند. اگر n يک متغير باشد، &n آدرس آن متغير است. از طرفي با استفاده از عملگر * ميتوان مقداري که در آدرس &n قرار گرفته را به دست آورد. بنابراين *&n برابر با خود n خواهد بود. همچنين اگر p يک اشارهگر باشد، *p مقداري که p به آن اشاره دارد را ميدهد. از طرفي با استفاده از عملگر & ميتوانيم آدرس چيزي که در *p قرار گرفته را بدست آوريم. پس &*p برابر با خود p خواهد بود. ترتيب قرارگرفتن اين عملگرها مهم است. يعني *&n با &*n برابر نيست. علت اين امر را توضيح دهيد.
اسلاید 344: عملگر * دو کاربرد دارد. اگر پسوندِ يک نوع باشد (مثل int*) يک اشارهگر به آن نوع را تعريف ميکند و اگر پيشوندِ يک اشارهگر باشد (مثل *p) آنگاه مقداري که p به آن اشاره ميکند را برميگرداند. عملگر & نيز دو کاربرد دارد. اگر پسوند يک نوع باشد (مثل int&) يک نام مستعار تعريف ميکند و اگر پيشوند يک متغير باشد (مثل &n) آدرس آن متغير را ميدهد.
اسلاید 345: 6- چپ مقدارها، راست مقدارهايک دستور جايگزيني دو بخش دارد: بخشي که در سمت چپ علامت جايگزيني قرار ميگيرد و بخشي که در سمت راست علامت جايگزيني قرار ميگيرد. مثلا دستور n = 55; متغير n در سمت چپ قرار گرفته و مقدار 55 در سمت راست. اين دستور را نميتوان به شکل 55 = n; نوشت زيرا مقدار 55 يک ثابت است و نميتواند مقدار بگيرد. پس هنگام استفاده از عملگر جايگزيني بايد دقت کنيم که چه چيزي را در سمت چپ قرار بدهيم و چه چيزي را در سمت راست.
اسلاید 346: چيزهايي که ميتوانند در سمت چپ جايگزيني قرار بگيرند «چپمقدار» خوانده ميشوند و چيزهايي که ميتوانند در سمت راست جايگزيني قرار بگيرند «راستمقدار» ناميده ميشوند. متغيرها (و به طور کلي اشيا) چپمقدار هستند و ليترالها (مثل 15 و ABC) راست مقدار هستند.
اسلاید 347: يک ثابت در ابتدا به شکل يک چپمقدار نمايان ميشود:const int MAX = 65535; // MAX is an lvalueاما از آن پس ديگر نميتوان به عنوان چپ مقدار از آنها استفاده کرد:MAX = 21024; // ERROR: MAX is constantبه اين گونه چپمقدارها، چپمقدارهاي «تغيير ناپذير» گفته ميشود. مثل آرايهها:int a[] = {1,2,3}; // O.Ka[] = {1,2,3}; // ERROR
اسلاید 348: مابقي چپمقدارها که ميتوان آنها را تغيير داد، چپمقدارهاي «تغيير پذير» ناميده ميشوند. هنگام اعلان يک ارجاع به يک چپمقدار نياز داريم:int& r = n; // O.K. n is an lvalueاما اعلانهاي زير غيرمعتبرند زيرا هيچ کدام چپمقدار نيستند:int& r = 44; // ERROR: 44 is not an lvalueint& r = n++; // ERROR: n++ is not an lvalueint& r = cube(n); // ERROR: cube(n) is not an lvalue1 – L_values2- R_valuesيک تابع، چپمقدار نيست اما اگر نوع بازگشتي آن يک ارجاع باشد، ميتوان تابع را به يک چپمقدار تبديل کرد.
اسلاید 349: 7- بازگشت از نوع ارجاعدر بحث توابع، ارسال از طريق مقدار و ارسال از طريق ارجاع را ديديم. اين دو شيوۀ تبادل در مورد بازگشت از تابع نيز صدق ميکند: بازگشت از طريق مقدار و بازگشت از طريق ارجاع. توابعي که تاکنون ديديم بازگشت به طريق مقدار داشتند. يعني هميشه يک مقدار به فراخواننده برميگشت. ميتوانيم تابع را طوري تعريف کنيم که به جاي مقدار، يک ارجاع را بازگشت دهد. مثلا به جاي اين که مقدار m را بازگشت دهد، يک ارجاع به m را بازگشت دهد.
اسلاید 350: وقتي بازگشت به طريق مقدار باشد، تابع يک راستمقدار خواهد بود زيرا مقدارها ليترال هستند و ليترالها راستمقدارند. به اين ترتيب تابع را فقط در سمت راست يک جايگزيني ميتوان به کار برد مثل: m = f(); وقتي بازگشت به طريق ارجاع باشد، تابع يک چپمقدار خواهد بود زيرا ارجاعها چپمقدار هستند. در اين حالت تابع را ميتوان در سمت چپ يک جايگزيني قرار داد مثل : f() = m;
اسلاید 351: براي اين که نوع بازگشتي تابع را به ارجاع تبديل کنيم کافي است عملگر ارجاع را به عنوان پسوند نوع بازگشتي درج کنيم.* مثال 7-8 بازگشت از نوع ارجاعint& max(int& m, int& n) { return (m > n ? m : n);}int main(){ int m = 44, n = 22; cout << m << , << n << , << max(m,n) << endl; max(m,n) = 55; cout << m << , << n << , << max(m,n) << endl;}44, 22, 4455, 22, 55
اسلاید 352: تابع max() از بين m و n مقدار بزرگتر را پيدا کرده و سپس ارجاعي به آن را باز ميگرداند. بنابراين اگر m از n بزرگتر باشد، تابع max(m,n) آدرس m را برميگرداند. پس وقتي مينويسيم max(m,n) = 55; مقدار 55 در حقيقت درون متغير m قرار ميگيرد (اگر m>n باشد). به بياني ساده، فراخواني max(m,n) خود m را بر ميگرداند نه مقدار آن را.
اسلاید 353: اخطار: وقتي يک تابع پايان مييابد، متغيرهاي محلي آن نابود ميشوند. پس هيچ وقت ارجاعي به يک متغير محلي بازگشت ندهيد زيرا وقتي کار تابع تمام شد، آدرس متغيرهاي محلياش غير معتبر ميشود و ارجاع بازگشت داده شده ممکن است به يک مقدار غير معتبر اشاره داشته باشد. تابع max() در مثال بالا يک ارجاع به m يا n را بر ميگرداند. چون m و n خودشان به طريق ارجاع ارسال شدهاند، پس محلي نيستند و بازگرداندن ارجاعي به آنها خللي در برنامه وارد نميکند.
اسلاید 354: به اعلان تابع max() دقت کنيد:int& max(int& m, int& n)نوع بازگشتي آن با استفاده از عملگر ارجاع & به شکل يک ارجاع درآمده است.مثال 9-7 به کارگيري يك تابع به عنوان عملگر زيرنويس آرايه
اسلاید 355: float& component(float* v, int k){ return v[k-1];}int main(){ float v[4]; for (int k = 1; k <= 4; k++) component(v,k) = 1.0/k; for (int i = 0; i < 4; i++) cout << v[ << i << ] = << v[i] << endl;} v[0] = 1 v[1] = 0.5 v[2] = 0.333333 v[3] = 0.25
اسلاید 356: تابع component() باعث ميشود که ايندکس آرايه v از «شمارهگذاري از صفر» به «شمارهگذاري از يک» تغيير کند. بنابراين component(v,3) معادل v[2] است. اين کار از طريق بازگشت از طريق ارجاع ممکن شده است.
اسلاید 357: 8- آرايهها و اشارهگرهاگرچه اشارهگرها از انواع عددي صحيح نيستند اما بعضي از اعمال حسابي را ميتوان روي اشارهگرها انجام داد. حاصل اين ميشود که اشارهگر به خانۀ ديگري از حافظه اشاره ميکند. اشارهگرها را ميتوان مثل اعداد صحيح افزايش و يا کاهش داد و ميتوان يک عدد صحيح را به آنها اضافه نمود يا از آن کم کرد. البته ميزان افزايش يا کاهش اشارهگر بستگي به نوع دادهاي دارد که اشارهگر به آن اشاره دارد. مثال 10-7 پيمايش آرايه با استفاده از اشارهگراين مثال نشان ميدهد كه چگونه ميتوان از اشارهگر براي پيمايش يک آرايه استفاده نمود:
اسلاید 358: int main(){ const int SIZE = 3; short a[SIZE] = {22, 33, 44}; cout << a = << a << endl; cout << sizeof(short) = << sizeof(short) << endl; short* end = a + SIZE; // converts SIZE to offset 6 short sum = 0; for (short* p = a; p < end; p++) { sum += *p; cout << t p = << p; cout << t *p = << *p; cout << t sum = << sum << endl; } cout << end = << end << endl;} a = 0x3fffd1a sizeof(short) = 2 p = 0x3fffd1a *p = 22 sum = 22 p = 0x3fffd1c *p = 33 sum = 55 p = 0x3fffd1e *p = 44 sum = 99 end = 0x3fffd20
اسلاید 359: اين مثال نشان ميدهد که هر گاه يک اشارهگر افزايش يابد، مقدار آن به اندازۀ تعداد بايتهاي شيئي که به آن اشاره ميکند، افزايش مييابد. مثلا اگر p اشارهگري به double باشد و sizeof(double) برابر با هشت بايت باشد، هر گاه که p يک واحد افزايش يابد، اشارهگر p هشت بايت به پيش ميرود.
اسلاید 360: مثلا کد زير :float a[8];float* p = a; // p points to a[0]++p; // increases the value of p by sizeof(float)
اسلاید 361: اگر floatها 4 بايت را اشغال كنند آنگاه ++p مقدار درون p را 4 بايت افزايش ميدهد و p += 5; مقدار درون p را 20 بايت افزايش ميدهد. با استفاده از خاصيت مذکور ميتوان آرايه را پيمايش نمود: يک اشارهگر را با آدرس اولين عنصر آرايه مقداردهي کنيد، سپس اشارهگر را پي در پي افزايش دهيد. هر افزايش سبب ميشود که اشارهگر به عنصر بعدي آرايه اشاره کند. يعني اشارهگري که به اين نحو به کار گرفته شود مثل ايندکس آرايه عمل ميکند.
اسلاید 362: همچنين با استفاده از اشارهگر ميتوانيم مستقيما به عنصر مورد نظر در آرايه دستيابي کنيم:float* p = a; // p points to a[0]p += 5; // now p points to a[5]يک نکتۀ ظريف در ارتباط با آرايهها و اشارهگرها وجود دارد: اگر اشارهگر را بيش از ايندکس آرايه افزايش دهيم، ممکن است به بخشهايي از حافظه برويم که هنوز تخصيص داده نشدهاند يا براي کارهاي ديگر تخصيص يافتهاند. تغيير دادن مقدار اين بخشها باعث بروز خطا در برنامه و کل سيستم ميشود. هميشه بايد مراقب اين خطر باشيد.
اسلاید 363: کد زير نشان ميدهد که چطور اين اتفاق رخ ميدهد.float a[8];float* p = a[7]; // points to last element in the array++p; //now p points to memory past last element!*p = 22.2; // TROUBLE!مثال بعدي نشان ميدهد كه ارتباط تنگاتنگي بين آرايهها و اشارهگرها وجود دارد. نام آرايه در حقيقت يک اشارهگر ثابت (const) به اولين عنصر آرايه است. همچنين خواهيم ديد که اشارهگرها را مانند هر متغير ديگري ميتوان با هم مقايسه نمود.
اسلاید 364: * مثال 11-7 پيمايش عناصر آرايه از طريق آدرسint main(){ short a[] = {22, 33, 44, 55, 66}; cout << a = << a << , *a = << *a << endl; for (short* p = a; p < a +5; p++) cout << p = << p << , *p = << *p << endl;} a = 0x3fffd08, *a = 22 p = 0x3fffd08, *p = 22 p = 0x3fffd0a, *p = 33 p = 0x3fffd0c, *p = 44 p = 0x3fffd0e, *p = 55 p = 0x3fffd10, *p = 66 p = 0x3fffd12, *p = 77
اسلاید 365: در نگاه اول، a و p مانند هم هستند: هر دو به نوع short اشاره ميکنند و هر دو داراي مقدار 0x3fffd08 هستند. اما a يک اشارهگر ثابت است و نميتواند افزايش يابد تا آرايه پيمايش شود. پس به جاي آن p را افزايش ميدهيم تا آرايه را پيمايش کنيم. شرط (p < a+5) حلقه را خاتمه ميدهد. a+5 به شکل زير ارزيابي ميشود:0x3fffd08 + 5*sizeof(short) = 0x3fffd08 + 5*2 = 0x3fffd08 + 0xa = 0x3fffd12پس حلقه تا زماني که p < 0x3fffd12 باشد ادامه مييابد.
اسلاید 366: عملگر زيرنويس [] مثل عملگر مقداريابي * رفتار ميکند. هر دوي اينها ميتوانند به عناصر آرايه دسترسي مستقيم داشته باشند.a[0] == *aa[1] == *(a + 1)a[2] == *(a + 2) ... ...پس با استفاده از کد زير نيز ميتوان آرايه را پيمايش نمود:for (int i = 0; i < 8; i++) cout << *(a + i) << endl;
اسلاید 367: مثال 12-7 مقايسۀ الگودر اين مثال، تابع loc() در ميان n1 عنصر اول آرايۀ a1 به دنبال n2 عنصر اول آرايۀ a2 ميگردد. اگر پيدا شد، يک اشارهگر به درون a1 برميگرداند که a2 از آنجا شروع ميشود وگرنه اشارهگر NULL را برميگرداند.short* loc(short* a1, short* a2, int n1, int n2){ short* end1 = a1 + n1; for (short* p1 = a1; p1 <end1; p1++) if (*p1 == *a2) { for (int j = 0; j < n2; j++) if (p1[j] != a2[j]) break; if (j == n2) return p1; } return 0;}
اسلاید 368: int main(){ short a1[9] = {11, 11, 11, 11, 11, 22, 33, 44, 55}; short a2[5] = {11, 11, 11, 22, 33}; cout << Array a1 begins at locationt << a1 << endl; cout << Array a2 begins at locationt << a2 << endl; short* p = loc(a1, a2, 9, 5); if (p) { cout << Array a2 found at locationt << p << endl; for (int i = 0; i < 5; i++) cout << t << &p[i] << : << p[i] << t << &a2[i] << : << a2[i] << endl; } else cout << Not found.n;}
اسلاید 369: Array a1 begins at location 0x3fffd12 Array a2 begins at location 0x3fffd08 Array a2 found at location 0x3fffd16 0x3fffd16: 11 0x3fffd08: 11 0x3fffd18: 11 0x3fffd0a: 11 0x3fffd1a: 11 0x3fffd0c: 11 0x3fffd1c: 22 0x3fffd0e: 22 0x3fffd1e: 33 0x3fffd10: 33
اسلاید 370: 9-7 عملگر newوقتي يك اشارهگر شبيه اين اعلان شود:float* p; // p is a pointer to a floatيک فضاي چهاربايتي به p تخصيص داده ميشود (معمولا sizeof(float) چهار بايت است). حالا p ايجاد شده است اما به هيچ جايي اشاره نميکند زيرا هنوز آدرسي درون آن قرار نگرفته. به چنين اشارهگري اشارهگر سرگردان ميگويند. اگر سعي کنيم يک اشارهگر سرگردان را مقداريابي يا ارجاع کنيم با خطا مواجه ميشويم.
اسلاید 371: مثلا دستور:*p = 3.14159; // ERROR: no storage has been allocated for *Pخطاست. زيرا p به هيچ آدرسي اشاره نميکند و سيستم عامل نميداند که مقدار 3.14159 را کجا ذخيره کند. براي رفع اين مشکل ميتوان اشارهگرها را هنگام اعلان، مقداردهي کرد:float x = 0; // x cintains the value 0float* p = &x // now p points to x*p = 3.14159; // O.K. assigns this value to address that p points to
اسلاید 372: در اين حالت ميتوان به *p دستيابي داشت زيرا حالا p به x اشاره ميکند و آدرس آن را دارد. راه حل ديگر اين است که يک آدرس اختصاصي ايجاد شود و درون p قرار بگيرد. بدين ترتيب p از سرگرداني خارج ميشود. اين کار با استفاده از عملگر new صورت ميپذيرد:float* p;p = new float; // allocates storage for 1 float*p = 3.14159; // O.K. assigns this value to that storageدقت کنيد که عملگر new فقط خود p را مقداردهي ميکند نه آدرسي که p به آن اشاره ميکند. ميتوانيم سه خط فوق را با هم ترکيب کرده و به شکل يک دستور بنويسيم:float* p = new float(3.141459);
اسلاید 373: با اين دستور، اشارهگر p از نوع float* تعريف ميشود و سپس يک بلوک خالي از نوع float منظور شده و آدرس آن به p تخصيص مييابد و همچنين مقدار 3.14159 در آن آدرس قرار ميگيرد. اگر عملگر new نتواند خانۀ خالي در حافظه پيدا کند، مقدار صفر را برميگرداند. اشارهگري که اين چنين باشد، «اشارهگر تهي» يا NULL مينامند.
اسلاید 374: با استفاده از کد هوشمند زير ميتوانيم مراقب باشيم که اشارهگر تهي ايجاد نشود:double* p = new double;if (p == 0) abort(); // allocator failed: insufficent memoryelse *p = 3.141592658979324;در اين قطعه کد، هرگاه اشارهگري تهي ايجاد شد، تابع abort() فراخواني شده و اين دستور لغو ميشود.
اسلاید 375: تاکنون دانستيم که به دو طريق ميتوان يک متغير را ايجاد و مقداردهي کرد. روش اول:float x = 3.14159; // allocates named memoryو روش دوم:float* p = new float(3.14159); // allocates unnamed memoryدر حالت اول، حافظۀ مورد نياز براي x هنگام کامپايل تخصيص مييابد. در حالت دوم حافظۀ مورد نياز در زمان اجرا و به يک شيء بينام تخصيص مييابد که با استفاده از *p قابل دستيابي است.
اسلاید 376: 10- عملگر deleteعملگر delete عملي برخلاف عملگر new دارد. کارش اين است که حافظۀ اشغال شده را آزاد کند. وقتي حافظهاي آزاد شود، سيستم عامل ميتواند از آن براي کارهاي ديگر يا حتي تخصيصهاي جديد استفاده کند. عملگر delete را تنها روي اشارهگرهايي ميتوان به کار برد که با دستور new ايجاد شدهاند. وقتي حافظۀ يک اشارهگر آزاد شد، ديگر نميتوان به آن دستيابي نمود مگر اين که دوباره اين حافظه تخصيص يابد:float* p = new float(3.14159);delete p; // deallocates q*p = 2.71828; // ERROR: q has been deallocated
اسلاید 377: وقتي اشاره گر p در کد بالا آزاد شود، حافظهاي که توسط new به آن تخصيص يافته بود، آزاد شده و به ميزان sizeof(float) به حافظۀ آزاد اضافه ميشود. وقتي اشارهگري آزاد شد، به هيچ چيزي اشاره نميکند؛ مثل متغيري که مقداردهي نشده. به اين اشارهگر، اشارهگر سرگردان ميگويند.اشارهگر به يک شيء ثابت را نميتوان آزاد کرد:const int* p = new int;delete p; // ERROR: cannot delete pointer to const objectsعلت اين است که «ثابتها نميتوانند تغيير کنند».
اسلاید 378: اگر متغيري را صريحا اعلان کردهايد و سپس اشارهگري به آن نسبت دادهايد، از عملگر delete استفاده نکنيد. اين کار باعث اشتباه غير عمدي زير ميشود:float x =3.14159; // x contains the value 3.14159float* p = &x; // p contains the address of xdelete p; // WARNING: this will make x freeکد بالا باعث ميشود که حافظۀ تخصيصيافته براي x آزاد شود. اين اشتباه را به سختي ميتوان تشخيص داد و اشکالزدايي کرد.
اسلاید 379: 11- آرايههاي پويانام آرايه در حقيقت يك اشارهگر ثابت است كه در زمان كامپايل، ايجاد و تخصيص داده ميشود:float a[20]; //a is a const pointer to a block of 20 floatsfloat* const p = new float[20]; // so is pهم a و هم p اشارهگرهاي ثابتي هستند که به بلوکي حاوي 20 متغير float اشاره دارند. به اعلان a بستهبندي ايستا1 ميگويند زيرا اين کد باعث ميشود که حافظۀ مورد نياز براي a در زمان کامپايل تخصيص داده شود. وقي برنامه اجرا شود، به هر حال حافظۀ مربوطه تخصيص خواهد يافت حتي اگر از آن هيچ استفادهاي نشود.
اسلاید 380: ميتوانيم با استفاده از اشارهگر، آرايۀ فوق را طوري تعريف کنيم که حافظه مورد نياز آن فقط در زمان اجرا تخصيص يابد:float* p = new float[20];دستور بالا، 20 خانۀ خالي حافظه از نوع float را در اختيار گذاشته و اشارهگر p را به خانۀ اول آن نسبت ميدهد. به اين آرايه، «آرايۀ پويا2» ميگويند. به اين طرز ايجاد اشيا بستهبندي پويا3 يا «بستهبندي زمان جرا» ميگويند.
اسلاید 381: آرايۀ ايستاي a و آرايۀ پوياي p را با يکديگر مقايسه کنيد. آرايۀ ايستاي a در زمان کامپايل ايجاد ميشود و تا پايان اجراي برنامه، حافظۀ تخصيصي به آن مشغول ميماند. ولي آرايۀ پوياي p در زمان اجرا و هر جا که لازم شد ايجاد ميشود و پس از اتمام کار نيز ميتوان با عملگر delete حافظۀ تخصيصي به آن را آزاد کرد:delete [] p;براي آزاد کردن آرايۀ پوياي p براکتها [] قبل از نام p بايد حتما قيد شوند زيرا p به يک آرايه اشاره دارد.
اسلاید 382: مثال 15-7 استفاده از آرايههاي پوياتابع get() در برنامۀ زير يک آرايۀ پويا ايجاد ميكند:void get(double*& a, int& n){ cout << Enter number of items: ; cin >> n; a = new double[n]; cout << Enter << n << items, one per line:n; for (int i = 0; i < n; i++) { cout << t << i+1 << : ; cin >> a[i]; }}void print(double* a, int n){ for (int i = 0; i < n; i++) cout << a[i] << ; cout << endl;}
اسلاید 383: int main(){ double* a;// a is simply an unallocated pointer int n; get(a,n); // now a is an array of n doubles print(a,n); delete [] a;// now a is simply an unallocated pointer again get(a,n); // now a is an array of n doubles print(a,n);}
اسلاید 384: Enter number of items: 4Enter 4 items, one per line: 1: 44.4 2: 77.7 3: 22.2 4: 88.844.4 77.7 22.2 88.8Enter number of items: 2Enter 2 items, one per line: 1: 3.33 2: 9.993.33 9.99
اسلاید 385: 12- اشارهگر ثابت«اشارهگر به يک ثابت» با «اشارهگر ثابت» تفاوت دارد. اين تفاوت در قالب مثال زير نشان داده شده است.مثال 16-7 اشارهگرهاي ثابت و اشارهگرهايي به ثابتهادر اين کد چهار اشارهگر اعلان شده. اشارهگر p، اشارهگر ثابت cp، اشاره به يک ثابت pc، اشارهگر ثابت به يک ثابت cpc :
اسلاید 386: int n = 44; // an intint* p = &n; // a pointer to an int++(*p); // OK: increments int *p++p; // OK: increments pointer pint* const cp = &n; // a const pointer to an int++(*cp); // OK: increments int *cp++cp; // illegal: pointer cp is constconst int k = 88; // a const intconst int * pc = &k; // a pointer to a const int++(*pc); // illegal: int *pc is const++pc; // OK: increments pointer pcconst int* const cpc = &k; // a const pointer to a const int++(*cpc); // illegal: int *pc is const++cpc; // illegal: pointer cpc is const
اسلاید 387: اشارهگر p اشارهگري به متغير n است. هم خود p قابل افزايش است (++p) و هم مقداري که p به آن اشاره ميکند قابل افزايش است (++(*P)). اشاره گر cp يک اشارهگر ثابت است. يعني آدرسي که در cp است قابل تغيير نيست ولي مقداري که در آن آدرس است را ميتوان دستکاري کرد. اشارهگر pc اشارهگري است که به آدرس يک ثابت اشاره دارد. خود pc را ميتوان تغيير داد ولي مقداري که pc به آن اشاره دارد قابل تغيير نيست. در آخر هم cpc يک اشارهگر ثابت به يک شيء ثابت است. نه مقدار cpc قابل تغيير است و نه مقداري که آدرس آن در cpc است.
اسلاید 388: 13- آرايهاي از اشارهگرها ميتوانيم آرايهاي تعريف کنيم که اعضاي آن از نوع اشارهگر باشند. مثلا دستور:float* p[4];آرايۀ p را با چهار عنصر از نوع float* (يعني اشارهگري به float) اعلان ميکند. عناصر اين آرايه را مثل اشارهگرهاي معمولي ميتوان مقداردهي کرد:p[0] = new float(3.14159);p[1] = new float(1.19);
اسلاید 389: اين آرايه را مي توانيم شبيه شکل مقابل مجسم کنيم:مثال بعد نشان ميدهد که آرايهاي از اشارهگرها به چه دردي ميخورد. از اين آرايه ميتوان براي مرتبکردن يک فهرست نامرتب به روش حبابي استفاده کرد. به جاي اين که خود عناصر جابجا شوند، اشارهگرهاي آنها جابجا ميشوند. 3.14159double1.19double0123p
اسلاید 390: مثال 17-7 مرتبسازي حبابي غيرمستقيمvoid sort(float* p[], int n){ float* temp; for (int i = 1; i < n; i++) for (int j = 0; j < n-i; j++) if (*p[j] > *p[j+1]) { temp = p[j]; p[j] = p[j+1]; p[j+1] = temp; }}
اسلاید 391: تابع sort() آرايهاي از اشارهگرها را ميگيرد. سپس درون حلقههاي تودرتوي for بررسي ميکند که آيا مقاديري که اشارهگرهاي مجاور به آنها اشاره دارند، مرتب هستند يا نه. اگر مرتب نبودند، جاي اشارهگرهاي آنها را با هم عوض ميکند. در پايان به جاي اين که يک فهرست مرتب داشته باشيم، آرايهاي داريم که اشارهگرهاي درون آن به ترتيب قرار گرفته اند.
اسلاید 392: 14-7 اشارهگري به اشارهگر ديگريك اشارهگر ميتواند به اشارهگر ديگري اشاره کند. مثلا:char c = t;char* pc = &c;char** ppc = &pc;char*** pppc = &ppc;***pppc = w; // changes value of c to wحالا pc اشارهگري به متغير کاراکتري c است. ppc اشارهگري به اشارهگر pc است و اشارهگر pppc هم به اشارهگر ppc اشاره دارد. مثل شکل مقابل:
اسلاید 393: pppcppcpctcبا اين وجود ميتوان با اشارهگر pppc مستقيما به متغير c رسيد.
اسلاید 394: 15- اشارهگر به توابعاين بخش ممکن است کمي عجيب به نظر برسد. حقيقت اين است که نام يک تابع مثل نام يک آرايه، يک اشارهگر ثابت است. نام تابع، آدرسي از حافظه را نشان ميدهد که کدهاي درون تابع در آن قسمت جاي گرفتهاند. پس بنابر قسمت قبل اگر اشارهگري به تابع اعلان کنيم، در اصل اشارهگري به اشارهگر ديگر تعريف کردهايم. اما اين تعريف، نحو متفاوتي دارد:int f(int); // declares function fint (*pf)(int); // declares function pointer pfpf = &f; // assigns address of f to pf
اسلاید 395: اشارهگر pf همراه با * درون پرانتز قرار گرفته، يعني اين که pf اشارهگري به يک تابع است. بعد از آن يک int هم درون پرانتز آمده است، به اين معني که تابعي که pf به آن اشاره مينمايد، پارامتري از نوع int دارد. اشارهگر pf را ميتوانيم به شکل زير تصور کنيم:
اسلاید 396: فايدۀ اشارهگر به توابع اين است که به اين طريق ميتوانيم توابع مرکب بسازيم. يعني ميتوانيم يک تابع را به عنوان آرگومان به تابع ديگر ارسال کنيم! اين کار با استفاده از اشارهگر به تابع امکان پذير است.pffint f(int n){...}
اسلاید 397: مثال 18-7 تابع مرکب جمعتابع sum() در اين مثال دو پارامتر دارد: اشارهگر تابع pf و عدد صحيح n :int sum(int (*)(int), int);int square(int);int cube(int);int main(){ cout << sum(square,4) << endl; // 1 + 4 + 9 + 16 cout << sum(cube,4) << endl; //1 + 8 + 27 + 64}
اسلاید 398: تابع sum() يک پارامتر غير معمول دارد. نام تابع ديگري به عنوان آرگومان به آن ارسال شده. هنگامي که sum(square,4) فراخواني شود، مقدار square(1)+square(2)+square(3)+square(4) بازگشت داده ميشود. چونsquare(k) مقدار k*k را برميگرداند، فراخواني sum(square,4) مقدار 1+4+9+16=30 را محاسبه نموده و بازميگرداند. تعريف توابع و خروجي آزمايشي به شکل زير است:
اسلاید 399: int sum(int (*pf)(int k), int n){ // returns the sum f(0) + f(1) + f(2) + ... + f(n-1): int s = 0; for (int i = 1; i <= n; i++) s += (*pf)(i); return s;}int square(int k){ return k*k;}int cube(int k){ return k*k*k;}30100
اسلاید 400: pf در فهرست پارامترهاي تابع sum() يک اشارهگر به تابع است. اشارهگر به تابعي که آن تابع پارامتري از نوع int دارد و مقداري از نوع int را برميگرداند. k در تابع sum اصلا استفاده نشده اما حتما بايد قيد شود تا کامپايلر بفهمد که pf به تابعي اشاره دارد که پارامتري از نوع int دارد. عبارت (*pf)(i) معادل با square(i) يا cube(i) خواهد بود، بسته به اين که کدام يک از اين دو تابع به عنوان آرگومان به sum() ارسال شوند.
اسلاید 401: نام تابع، آدرس شروع تابع را دارد. پس square آدرس شروع تابع square() را دارد. بنابراين وقتي تابع sum() به شکل sum(square,4) فراخواني شود، آدرسي که درون square است به اشارهگر pf فرستاده ميشود. با استفاده از عبارت (*pf)(i) مقدار i به آرگومان تابعي فرستاده ميشود که pf به آن اشاره دارد.
اسلاید 402: 16- NUL و NULL ثابت صفر (0) از نوع int است اما اين مقدار را به هر نوع بنيادي ديگر ميتوان تخصيص داد:char c = 0; // initializes c to the char 0short d = 0; // initializes d to the short int 0int n = 0; // initializes n to the int 0unsigned u = 0; // initializes u to the unsigned int 0float x = 0; // initializes x to the float 0.0double z = 0; // initializes z to the double 0.0
اسلاید 403: مقدار صفر معناهاي گوناگوني دارد. وقتي براي اشياي عددي به کار رود، به معناي عدد صفر است. وقتي براي اشياي کاراکتري به کار رود، به معناي کاراکتر تهي يا NUL است. NUL معادل کاراکتر 0 نيز هست. وقتي مقدار صفر براي اشارهگرها به کار رود، به معناي «هيچ چيز» يا NULL است. NULL يک کلمۀ کليدي است و کامپايلر آن را ميشناسد. هنگامي که مقدار NULL يا صفر در يک اشارهگر قرار ميگيرد، آن اشارهگر به خانه 0x0 در حافظه اشاره دارد. اين خانۀ حافظه، يک خانۀ استثنايي است که قابل پردازش نيست. نه ميتوان آن خانه را مقداريابي کرد و نه ميتوان مقداري را درون آن قرار داد. به همين دليل به NULL «هيچ چيز» ميگويند.
اسلاید 404: وقتي اشارهگري را بدون استفاده از new اعلان ميکنيم، خوب است که ابتدا آن را NULL کنيم تا مقدار زبالۀ آن پاک شود. اما هميشه بايد به خاطر داشته باشيم که اشارهگر NULL را نبايد مقداريابي نماييم:int* p = 0; // p points to NULL*p = 22; // ERROR: cannot dereference the NULL pointerپس خوب است هنگام مقداريابي اشارهگرها، احتياط کرده و بررسي کنيم که آن اشارهگر NULL نباشد:if (p) *p = 22; // O.K.حالا دستور *p=22; وقتي اجرا ميشود که p صفر نباشد. ميدانيد که شرط بالا معادل شرط زير است:if (p != NULL) *p = 22;
اسلاید 405: اشارهگرها را نميتوان ناديده گرفت. آنها سرعت پردازش را زياد ميکنند و کدنويسي را کم. با استفاده از اشارهگرها ميتوان به بهترين شکل از حافظه استفاده کرد. با به کارگيري اشارهگرها ميتوان اشيايي پيچيدهتر و کارآمدتر ساخت.
اسلاید 406: پايان جلسه هفتم
اسلاید 407: جلسه هشتم« رشتههاي كاراكتري و فايلها در ++C استاندارد»
اسلاید 408: مروري بر اشارهگرهارشتههاي كاراكتري در Cورودي/خروجي رشتههاي کاراکتريچند تابع عضو cin و coutتوابع كاراكتري C استانداردآرايهاي از رشتههاتوابع استاندارد رشتههاي کاراکتريآنچه در اين جلسه مي خوانيد›››
اسلاید 409: رشتههاي کاراکتري در C++ استانداردنگاهي دقيقتر به تبادل دادههاورودي قالببندي نشدهنوع string در ++C استانداردفايلهاهدف کلي: آشنايي با کلاسها و اصول اوليۀ بهکارگيري آنها.
اسلاید 410: هدف کلي: معرفي رشتههاي کاراکتري به سبک c و c++ و نحوۀ ايجاد و دستکاري آنها و همچنين نحوۀ استفاده از فايلهاي متني براي ذخيرهسازي و بازيابي اطلاعات.
اسلاید 411: هدفهاي رفتاري: انتظار ميرود پس از پايان اين جلسه بتوانيد:- رشتههاي کاراکتري به سبک C استاندارد را ايجاد نماييد.- توابع معرفي شده عضو cin و cout را شناخته و وظيفۀ هر يک را شرح دهيد.- رشتههاي کاراکتري به سبک C++ استاندارد را ايجاد نماييد.- مفهوم «ورودي قالببندي شده» و «ورودي قالببندي نشده» را دانسته و هر کدام را در مکانهاي مناسب به کار ببريد.- نوع string را شناخته و رشتههايي از اين نوع ايجاد کنيد و با استفاده از توابع خاص، اين رشتهها را دستکاري نماييد.- اطلاعات کاراکتري و رشتهاي را در يک فايل متني نوشته يا از آن بخوانيد.
اسلاید 412: مقدمه:دادههايي که در رايانهها پردازش ميشوند هميشه عدد نيستند. معمولا لازم است که اطلاعات کاراکتري مثل نام افراد – نشانيها – متون – توضيحات – کلمات و ... نيز پردازش گردند، جستجو شوند، مقايسه شوند، به يکديگر الصاق شوند يا از هم تفکيک گردند.در اين جلسه بررسي ميکنيم که چطور اطلاعات کاراکتري را از ورودي دريافت کنيم و يا آنها را به شکل دلخواه به خروجي بفرستيم. در همين راستا توابعي معرفي ميکنيم که انجام اين کارها را آسان ميکنند.
اسلاید 413: يك اشارهگر متغيري است که حاوي يک آدرس از حافظه ميباشد. نوع اين متغير از نوع مقداري است که در آن آدرس ذخيره شده. با استفاده از عملگر ارجاع & ميتوان آدرس يک شي را پيدا کرد. همچنين با استفاده از عملگر مقداريابي * ميتوانيم مقداري که در يک آدرس قرار دارد را مشخص کنيم. به تعاريف زير نگاه کنيد:مروري بر اشارهگرها:int n = 44;int* p = &n;
اسلاید 414: رشتههاي كاراكتري در Cدر زبان C++ يك «رشتۀ کاراکتري» آرايهاي از کاراکترهاست که اين آرايه داراي ويژگي مهم زير است:1- يك بخش اضافي در انتهاي آرايه وجود دارد که مقدار آن، کاراکتر NUL يعني 0‘ است. پس تعداد کل کاراکترها در آرايه هميشه يکي بيشتر از طول رشته است.2 – رشتۀ کاراکتري را ميتوان با ليترال رشتهاي به طور مستقيم مقدارگذاري کرد مثل: char str[] = string;توجه كنيد كه اين آرايه هفت عنصر دارد: s و t و r و i و n و g و 0
اسلاید 415: 3– کل يک رشتۀ کاراکتري را ميتوان مثل يک متغير معمولي چاپ کرد. مثل:cout << str;در اين صورت، همۀ کاراکترهاي درون رشتۀ کاراکتري str يکي يکي به خروجي ميروند تا وقتي که به کاراکتر انتهايي NUL برخورد شود.4 – يک رشتۀ کاراکتري را ميتوان مثل يک متغير معمولي از ورودي دريافت کرد مثل:cin >> str;در اين صورت، همۀ کاراکترهاي وارد شده يکي يکي درون str جاي ميگيرند تا وقتي که به يک فضاي خالي در کاراکترهاي ورودي برخورد شود. برنامهنويس بايد مطمئن باشد که آرايۀ str براي دريافت همۀ کاراکترهاي وارد شده جا دارد.
اسلاید 416: 5 – توابع تعريف شده در سرفايل <cstring> را ميتوانيم براي دستکاري رشتههاي کاراکتري به کار بگيريم. اين توابع عبارتند از: تابع طول رشته strlen() توابع کپي رشته strcpy() و strncpy()توابع الصاق رشتهها strcat() و strncat() توابع مقايسۀ رشتهها strcmp() و strncmp() و تابع استخراج نشانه strtok() .
اسلاید 417: رشتههاي کاراکتري با كاراكتر NUL خاتمه مييابندبرنامۀ کوچک زير نشان ميدهد که کاراکتر 0 به رشتههاي کاراکتري الصاق ميشود:int main(){ char s[] = ABCD; for (int i = 0; i < 5; i++) cout << s[ << i << ] = << s[i] << n;}
اسلاید 418: رشتۀ کاراکتري s داراي پنج عضو است که عضو پنجم، کاراکتر 0 ميباشد. تصوير خروجي اين مطلب را تاييد مينمايد. وقتي کاراکتر 0 به cout فرستاده ميشود، هيچ چيز چاپ نميشود. حتي جاي خالي هم چاپ نميشود. خط آخر خروجي، عضو پنجم را نشان مي دهد که ميان دو علامت آپستروف هيچ چيزي چاپ نشده.S0A1B2C3D4Ø
اسلاید 419: ورودي/خروجي رشتههاي کاراکتري:در C++ به چند روش ميتوان رشتههاي کاراکتري را دريافت کرده يا نمايش داد. يک راه استفاده از عملگرهاي کلاس string است که در بخشهاي بعدي به آن خواهيم پرداخت. روش ديگر، استفاده از توابع کمکي است که آن را در ادامه شرح ميدهيم.
اسلاید 420: مثال 2-8 روش سادۀ دريافت و نمايش رشتههاي کاراکتري:در برنامۀ زير يک رشتۀ کاراکتري به طول 79 کاراکتر اعلان شده و کلماتي که از ورودي خوانده ميشود در آن رشته قرار ميگيرد:int main(){ char word[80]; do { cin >> word; if (*word) cout << t << word << n; } while (*word);}
اسلاید 421: چند تابع عضو cin و coutبه cin شيء فرآيند ورودي ميگويند. اين شي شامل توابع زير است: همۀ اين توابع شامل پيشوند cin هستند زيرا آنها عضوي از cin ميباشند. به cout شيء فرآيند خروجي ميگويند. اين شي نيز شامل تابع cout.put() است. نحوۀ کاربرد هر يک از اين توابع عضو را در ادامه خواهيم ديد.فراخواني cin.getline(str,n); باعث ميشود که n کاراکتر به درون str خوانده شود و مابقي کاراکترهاي وارد شده ناديده گرفته ميشوند.cin.getline() cin.get() cin.ignore() cin.putback()cin.peek()
اسلاید 422: با دو پارامتر cin.getline() تابعاين برنامه ورودي را خط به خط به خروجي ميفرستد:int main(){ char line[80]; do { cin.getline(line,80); if (*line) cout << t[ << line << ]n; } while (*line);}
اسلاید 423: با سه پارامتر cin.getlineتابع() برنامه زير، متن ورودي را جمله به جمله تفکيک مينمايد: int main(){ char clause[20]; do { cin.getline(clause, 20, ,); if (*clause) cout << t[ << clause << ]n; } while (*clause);}
اسلاید 424: تابع cin.get()اين برنامه تعداد حرف e در جريان ورودي را شمارش ميكند. تا وقتي cin.get(ch) کاراکترها را با موفقيت به درون ch ميخواند، حلقه ادامه مييابد:int main(){ char ch; int count = 0; while (cin.get(ch)) if (ch = = e) ++count; cout << count << es were counted.n;}
اسلاید 425: تابع cout.put()برنامۀ زير، اولين حرف از هر کلمۀ ورودي را به حرف بزرگ تبديل کرده و آن را مجددا در خروجي چاپ ميکند:int main(){ char ch, pre = 0; while (cin.get(ch)) { if (pre = = || pre = = n) cout.put(char(toupper(ch))); else cout.put(ch); pre = ch; }}
اسلاید 426: cin.ignore() و cin.putback() توابعبا استفاده از برنامۀ زير، تابعي آزمايش ميشود که اين تابع اعداد صحيح را از ورودي استخراج ميکند: int nextInt();int main(){ int m = nextInt(), n = nextInt(); cin.ignore(80,n); // ignore rest of input line cout << m << + << n << = << m+n << endl;}int nextInt(){ char ch; int n; while (cin.get(ch)) if (ch >= 0 && ch <= 9) // next character is a digit { cin.putback(ch); // put it back so it can be cin >> n; // read as a complite int break; } return n;}
اسلاید 427: تابع cin.peek()int nextInt(){ char ch; int n; while (ch = cin.peek()) if (ch >= 0 && ch <= 9) { cin >> n; break; } else cin.get(ch); return n;}اين نسخه از تابع nextInt() معادل آن است كه در مثال قبلي بود:
اسلاید 428: توابع كاراكتري C استاندارددر مثال 6-8 به تابعtoupper() اشاره شد. اين فقط يکي از توابعي است که براي دستکاري کاراکترها استفاده ميشود. ساير توابعي که در سرفايل <ctype.h> يا <cctype> تعريف شده به شرح زير است:شرحنام تابعint isalnum(int c);اگر c کاراکتر الفبايي يا عددي باشد مقدار غيرصفر وگرنه صفر را برميگرداندisalnum()int isalpha(int c);اگر c کاراکتر الفبايي باشد مقدار غيرصفر و در غير آن، صفر را برميگرداندisalpha()
اسلاید 429: شرحنام تابعint iscntrl(int c);اگر c کاراکتر کنترلي باشد مقدار غيرصفر و در غير آن، صفر را برميگرداندiscntrl()int isdigit(int c);اگر c کاراکتر عددي باشد، مقدار غيرصفر و در غير آن، صفر را برميگرداندisdigit()int isgraph(int c);اگر c کاراکتر چاپي و غيرخالي باشد مقدار غيرصفر وگرنه صفر را برميگرداندisgraph()int islower(int c);اگر c حرف کوچک باشد مقدار غيرصفر و در غير آن، صفر را برميگرداندislower()int isprint(int c);اگر c کاراکتر قابل چاپ باشد مقدار غيرصفر و در غير آن، صفر را برميگرداندisprint()
اسلاید 430: شرحنام تابعint ispunct(int c);اگر c کاراکتر چاپي به غير از حروف و اعداد و فضاي خالي باشد، مقدار غيرصفر برميگرداند وگرنه مقدار صفر را برميگرداندispunct()int isspace(int c);اگر c کاراکتر فضاي سفيد شامل فضاي خالي و عبور فرم f و خط جديد n و بازگشت نورد r و پرش افقي t و پرش عمودي v باشد، مقدار غيرصفر را برميگرداند وگرنه صفر را برميگرداندisspace()int isupper(int c);اگر c حرف بزرگ باشد، مقدار غيرصفر برميگرداند وگرنه صفر را برميگرداندisupper()int isxdigit(int c);اگر c يکي از ده کاراکتر عددي يا يکي از دوازده حرف عدد شانزدهدهي شامل a و b و c و d و e و f و A و B و C و D و E و F باشد، مقدار غيرصفر برميگرداند وگرنه مقدار صفر را برميگرداندisxdigit()int tolower(int c);اگر c حرف بزرگ باشد، کاراکتر کوچک معادل آن را برميگرداند وگرنه خود c را برميگرداندtolower()int toupper(int c);اگر c حرف کوچک باشد، کاراکتر بزرگ معادل آن را برميگرداند وگرنه خود c را برميگرداندtoupper()
اسلاید 431: توجه کنيد که همۀ توابع فوق يک پارامتر از نوع int دريافت ميکنند و يک مقدار int را برميگردانند. علت اين است که نوع char در اصل يک نوع صحيح است. در عمل وقتي توابع فوق را به کار ميبرند، يک مقدار char به تابع ميفرستند و مقدار بازگشتي را نيز در يک char ذخيره ميکنند. به همين خاطر اين توابع را به عنوان «توابع کاراکتري» در نظر ميگيريم.
اسلاید 432: آرايهاي از رشتههابه خاطر داريد که گفتيم يک آرايۀ دوبعدي در حقيقت آرايهاي يک بعدي است که هر کدام از اعضاي آن يک آرايۀ يک بعدي ديگر است. مثلا در آرايۀ دو بعدي که به شکل مقابل اعلان شده باشد:char name[5][20]; اين آرايه در اصل پنج عضو دارد که هر عضو ميتواند بيست کاراکتر داشته باشد. اگر آرايۀ فوق را با تعريف رشتههاي کاراکتري مقايسه کنيم، نتيجه اين ميشود که آرايۀ بالا يک آرايۀ پنج عنصري است که هر عنصر آن يک رشتۀ کاراکتري بيست حرفي است. اين آرايه را ميتوانيم به شکل مقابل تصور کنيم.
اسلاید 433: از طريق name[0] و name[1] و name[2] و name[3] و name[4] ميتوانيم به هر يک از رشتههاي کاراکتري در آرايۀ بالا دسترسي داشته باشيم. يعني آرايۀ name گرچه به صورت يک آرايۀ دوبعدي اعلان شده ليکن به صورت يک آرايۀ يک بعدي با آن رفتار ميشود.
اسلاید 434: آرايهاي از رشتههاي کاراکتري برنامۀ زير چند رشتۀ کاراکتري را از ورودي ميخواند و آنها را در يک آرايه ذخيره کرده و سپس مقادير آن آرايه را چاپ ميکند:int main(){ char name[5][20]; int count=0; cout << Enter at most 4 names with at most 19 characters:n; while (cin.getline(name[count++], 20)) ; --count; cout << The names are:n; for (int i=0; i<count; i++) cout << t << i << . [ << name[i] << ] << endl;}
اسلاید 435: يك آرايۀ رشتهاي پويا اين برنامه نشان ميدهد که چگونه ميتوان از کاراکتر $ به عنوان کاراکتر نگهبان در تابع getline() استفاده کرد. مثال زير تقريبا معادل مثال 9-9 است. برنامۀ زير مجموعهاي از اسامي را ميخواند، طوري که هر اسم روي يک خط نوشته ميشود و هر اسم با کاراکتر n پايان مييابد. اين اسامي در آرايۀ name ذخيره ميشوند. سپس نامهاي ذخيره شده در آرايۀ name چاپ ميشوند:
اسلاید 436: int main(){ char buffer[80]; cin.getline(buffer,80,$); char* name[4]; name[0] = buffer; int count = 0; for (char* p=buffer; *p ! 0; p++) if (*p == n) { *p = 0; // end name[count] name[++count] = p+1; // begin next name } cout << The names are:n; for (int i=0; i<count; i++) cout << t << i << . [ << name[i] << ] << endl;}
اسلاید 437: مقداردهي يك آرايۀ رشتهاي اين برنامه هم آرايۀ رشتهاي name را مقداردهي کرده و سپس مقادير آن را چاپ مينمايد:int main(){char* name[]= { Mostafa Chamran, Mehdi Zeinoddin, Ebrahim Hemmat }; cout << The names are:n; for (int i = 0; i < 3; i++) cout << t << i << . [ << name[i] << ] << endl;}
اسلاید 438: توابع استاندارد رشتههاي کاراکتري:سرفايل <cstring> که به آن «کتابخانۀ رشتههاي کاراکتري» هم ميگويند، شامل خانوادۀ توابعي است که براي دستکاري رشتههاي کاراکتري خيلي مفيدند.مثال بعدي سادهترين آنها يعني تابع طول رشته را نشان ميدهد. اين تابع، طول يک رشتۀ کاراکتري ارسال شده به آن (يعني تعداد کاراکترهاي آن رشته) را برميگرداند.
اسلاید 439: تابع strlen():برنامۀ زير يک برنامۀ آزمون ساده براي تابع strlen() است. وقتي strlen(s) فراخواني ميشود، تعداد کاراکترهاي درون رشتۀ s که قبل از کاراکتر NUL قرار گرفتهاند، بازگشت داده ميشود:#include <cstring>int main(){ char s[] = ABCDEFG; cout << strlen( << s << ) = << strlen(s) << endl; cout << strlen() = << strlen() << endl; char buffer[80]; cout << Enter string: ; cin >> buffer; cout << strlen( << buffer << ) = << strlen(buffer) << endl;}
اسلاید 440: توابع strrchr(), strchr(), strstr():برنامۀ زير، مکانيابي يک کاراکتر يا زيررشتۀ خاص را در رشتۀ کاراکتري s نشان ميدهد:#include <cstring>int main(){ char s[] = The Mississippi is a long river.; cout << s = << s << n; char* p = strchr(s, ); cout << strchr(s, ) points to s[ << p - s << ].n; p = strchr(s, e); cout << strchr(s, e) points to s[ << p - s << ].n; p = strrchr(s, e); cout << strrchr(s, e) points to s[ << p - s << ].n; p = strstr(s, is); cout << strstr(s, is) points to s[ << p – s << ].n; p = strstr(s, isi); cout << strstr(s, is) points to s[ << p – s << ].n; if (p == NULL) cout << strstr(s, isi) returns NULLn;}
اسلاید 441: تابع strcpy():برنامۀ زير نشان ميدهد که فراخواني strcpy(s1, s2) چه تاثيري دارد:#include <iostream>#include <cstring>int main(){ char s1[] = ABCDEFG; char s2[] = XYZ; cout << Before strcpy(s1,s2):n; cout << ts1 = [ << s1 << ], length = << strlen(s1) << endl; cout << ts2 = [ << s2 << ], length = << strlen(s2) << endl; strcpy(s1,s2); cout << After strcpy(s1,s2):n; cout << ts1 = [ << s1 << ], length = << strlen(s1) << endl; cout << ts2 = [ << s2 << ], length = << strlen(s2) << endl;}
اسلاید 442: تابع :strncpy()برنامۀ زير بررسي ميکند که فراخوانيstrncpy(s1, s2, n) چه اثري دارد: int main(){ char s1[] = ABCDEFG; char s2[] = XYZ; cout << Before strncpy(s1,s2,2):n; cout << ts1 = [ << s1 << ], length = << strlen(s1) << endl; cout << ts2 = [ << s2 << ], length = << strlen(s2) << endl; strncpy(s1,s2,2); cout << After strncpy(s1,s2,2):n; cout << ts1 = [ << s1 << ], length = << strlen(s1) << endl; cout << ts2 = [ << s2 << ], length = << strlen(s2) << endl;}
اسلاید 443: تابع الصاق رشته :strcat()برنامۀ زير بررسي ميکند که فراخواني strcat(s1, s2) چه تاثيري دارد: int main(){ char s1[] = ABCDEFG; char s2[] = XYZ; cout << Before strcat(s1,s2):n; cout << ts1 = [ << s1 << ], length = << strlen(s1) << endl; cout << ts2 = [ << s2 << ], length = << strlen(s2) << endl; strcat(s1,s2); cout << After strcat(s1,s2):n; cout << ts1 = [ << s1 << ], length = << strlen(s1) << endl; cout << ts2 = [ << s2 << ], length = << strlen(s2) << endl;}
اسلاید 444: رشتههاي کاراکتري در C++ استاندارد :رشتههاي کاراکتري که تاکنون تشريح شد، در زبان C استفاده ميشوند و البته بخش مهمي از C++ نيز محسوب ميشوند زيرا وسيلۀ مفيدي براي پردازش سريع دادهها هستند. اما اين سرعت پردازش، هزينهاي هم دارد: خطر خطاهاي زمان اجرا. ين خطاها معمولا از اين ناشي ميشوند که فقط بر کاراکتر NUL به عنوان پايان رشته تکيه ميشود. C++ رشتههاي کاراکتري خاصي نيز دارد که امنتر و مطمئنتر هستند. در اين رشتهها، طول رشته نيز درون رشته ذخيره ميشود و لذا فقط به کاراکتر NUL براي مشخص نمودن انتهاي رشته اکتفا نميشود.
اسلاید 445: نگاهي دقيقتر به تبادل دادههاوقتي ميخواهيم دادههايي را وارد کنيم، اين دادهها را در قالب مجموعهاي از کاراکترها تايپ ميکنيم. همچنين وقتي ميخواهيم نتايجي را به خارج از برنامه بفرستيم، اين نتايج در قالب مجموعهاي از کاراکترها نمايش داده ميشوند. لازم است که اين کاراکترها به نحوي براي برنامه تفسير شوند. مثلا وقتي قصد داريم يک عدد صحيح را وارد کنيم، چند کاراکتر عددي تايپ ميکنيم
اسلاید 446: حالا ساز و کاري لازم است که از اين کاراکترها يک مقدار صحيح بسازد و به برنامه تحويل دهد. همچنين وقتي قصد داريم يک عدد اعشاري را به خروجي بفرستيم، بايد با استفاده از راهکاري، آن عدد اعشاري به کاراکترهايي تبديل شود تا در خروجي نمايش يابد. C++ بر عهده دارند.
اسلاید 447: جريانها اين وظايف را در C++ بر عهده دارند. جريانها شبيه پالايهاي هستند که دادهها را به کاراکتر تبديل ميکنند و کاراکترها را به دادههايي از يک نوع بنيادي تبديل مينمايند. به طور کلي، وروديها و خروجيها را يک کلاس جريان به نام stream کنترل ميکند. اين کلاس خود به زيرکلاسهايي تقسيم ميشود:
اسلاید 448: شيء istream جرياني است که دادههاي مورد نياز را از کاراکترهاي وارد شده از صفحه کليد، فراهم ميکند. شيء ostream جرياني است که دادههاي حاصل را به کاراکترهاي خروجي قابل نمايش روي صفحۀ نمايشگر تبديل مينمايد. شيء ifstream جرياني است که دادههاي مورد نياز را از دادههاي داخل يک فايل، فراهم ميکند. شيء ofstream جرياني است که دادههاي حاصل را درون يک فايل ذخيره مينمايد. اين جريانها و طريقۀ استفاده از آنها را در ادامه خواهيم ديد.
اسلاید 449: استفاده از عملگر بيرونكشي براي كنترل کردن يك حلقه :int main(){ int n; while (cin >> n) cout << n = << n << endl;}
اسلاید 450: ورودي قالببندي نشده:سرفايل <iostream> توابع مختلفي براي ورودي دارد. اين توابع براي وارد کردن کاراکترها و رشتههاي کاراکتري به کار ميروند که کاراکترهاي فضاي سفيد را ناديده نميگيرند. رايجترين آنها، تابع cin.get() براي دريافت يک کاراکتر تکي و تابع cin.getline() براي دريافت يک رشتۀ کاراکتري است.
اسلاید 451: دريافت كاراكترها با استفاده از تابع :cin.get() while (cin.get(c)){ if (c >= a && c <= z) c += A - a; // capitalize c cout.put(c);}
اسلاید 452: وارد كردن يک رشتۀ کاراکتري به وسيلۀ تابع :cin.getline()برنامۀ زير نشان ميدهد که چطور ميتوان دادههاي متني را خط به خط از ورودي خوانده و درون يک آرايۀ رشتهاي قرار داد:::const int LEN=32; // maximum word lengthconst int SIZE=10; // array sizetypedef char Name[LEN]; // defines Name to be a C_string typeint main(){ Name martyr[SIZE]; // defines martyr to be an array of 10 names int n=0; while(cin.getline(martyr[n++], LEN) && n<SIZE) ; --n; for (int i=0; i<n; i++) cout << t << i+1 << . << martyr[i] << endl;}
اسلاید 453: نوع string در ++C استاندارد:در C++ استاندارد نوع دادهاي خاصي به نام string وجود دارد که مشخصات اين نوع در سرفايل <string> تعريف شده است. براي آشنايي با اين نوع جديد، از طريقۀ اعلان و مقداردهي آن شروع ميکنيم. اشيايي که از نوع string هستند به چند طريق ميتوانند اعلان و مقداردهي شوند:string s1; // s1 contains 0 charactersstring s2 = PNU University; // s2 contains 14 charactersstring s3(60, *); // s3 contains 60 asterisksstring s4 = s3; // s4 contains 60 asterisksstring s5(s2, 4, 2); // s5 is the 2-character string Un
اسلاید 454: استفاده از نوع stringکد زير يک مجموعه کاراکتر را از ورودي ميگيرد و سپس بعد از هر کاراکتر E يک علامت ويرگول , اضافه مينمايد. مثلا اگر عبارت The SOFTWARE MOVEMENT is began وارد شود، برنامۀ زير، آن را به جملۀ زير تبديل ميکند: The SOFTWARE, MOVE,ME,NT is began متن برنامه اين چنين است: string word;int k;while (cin >> word){ k = word.find(E) + 1; if (k < word.length()) word.relace(k, 0, ,); cout << word << ;}
اسلاید 455: فايلها يکي از مزيتهاي رايانه، قدرت نگهداري اطلاعات حجيم است. فايلها اين قدرت را به رايانه ميدهند. اگر چيزي به نام فايل وجود نميداشت، شايد رايانهها به شکل امروزي توسعه و کاربرد پيدا نميکردند. چون اغلب برنامههاي امروزي با فايلها سر و کار دارند، يک برنامهنويس لازم است که با فايل آشنا باشد و بتواند با استفاده از اين امکان ذخيره و بازيابي، کارايي برنامههايش را ارتقا دهد.
اسلاید 456: پردازش فايل در C++ بسيار شبيه تراکنشهاي معمولي ورودي و خروجي است زيرا اينها همه از اشياي جريان مشابهي بهره ميبرند. جريان fstream براي تراکنش برنامه با فايلها به کار ميرود. fstream نيز به دو زيرشاخۀ ifstream و ofstream تقسيم ميشود. جريان ifstream براي خواندن اطلاعات از يک فايل به کار ميرود و جريان ofstream براي نوشتن اطلاعات درون يک فايل استفاده ميشود.
اسلاید 457: فراموش نکنيد که اين جريانها در سرفايل <fstream> تعريف شدهاند. پس بايد دستور پيشپردازندۀ #include <fstream> را به ابتداي برنامه بيافزاييد. سپس ميتوانيد عناصري از نوع جريان فايل به شکل زير تعريف کنيد: ifstream readfile(INPUT.TXT);ofstream writefile(OUTPUT.TXT);طبق کدهاي فوق، readfile عنصري است که دادهها را از فايلي به نام INPUT.TXT ميخواند و writefile نيز عنصري است که اطلاعاتي را در فايلي به نام OUTPUT.TXT مينويسد. اکنون ميتوان با استفاده از عملگر >> دادهها را به درون readfile خواند و با عملگر << اطلاعات را درون writefile نوشت. به مثال زير توجه کنيد.
اسلاید 458: يک دفتر تلفنبرنامۀ زير، چند نام و تلفن مربوط به هر يک را به ترتيب از کاربر دريافت کرده و در فايلي به نام PHONE.TXT ذخيره ميکند. کاربر براي پايان دادن به ورودي بايد عدد 0 را تايپ کند. #include <fstream>#include <iostream>using namespace std;int main(){ ofstream phonefile(PHONE.TXT); long number; string name; cout << Enter a number for each name. (0 for quit): ; for ( ; ; ) { cout << Number: ; cin >> number; if (number == 0) break; phonefile << number << ; cout << Name: ; cin >> name; phonefile << name << ; cout << endl; }}
اسلاید 459: جستجوي يک شماره در دفتر تلفناين برنامه، فايل توليد شده توسط برنامۀ قبل را به کار ميگيرد و درون آن به دنبال يک شماره تلفن ميگردد:#include <fstream>#include <iostream>using namespace std;int main(){ ifstream phonefile(PHONE.TXT); long number; string name, searchname; bool found=false; cout << Enter a name for findind its phone number: ; cin >> searchname; cout << endl; while (phonefile >> number) { phonefile >> name; if (searchname == name) { cout << name << << number << endl; found = true; } if (!found) cout << searchname << is not in this phonebook. << endl;}
اسلاید 460: پايان جلسه هشتم
اسلاید 461: جلسه نهم«شيگرايي»
اسلاید 462: آنچه در اين جلسه مي خوانيد:1- اعلان كلاسها2- سازندهها 3- فهرست مقداردهي در سازندهها 4- توابع دستيابي 5- توابع عضو خصوصي 6- سازندۀ كپي ›››
اسلاید 463: 7- نابود کننده 8 - اشياي ثابت 9- اشارهگر به اشيا 10- اعضاي دادهاي ايستا 11- توابع عضو ايستا
اسلاید 464: هدف کلي :آشنايي با کلاسها و اصول اوليۀ بهکارگيري آنها.
اسلاید 465: هدفهاي رفتاري: انتظار ميرود پس از پايان اين جلسه بتوانيد:- نحوۀ اعلان «کلاسها» را بدانيد و اعضاي يک کلاس را برشماريد.- تفاوت بين اعضاي «عمومي» و «خصوصي» را شرح داده و نحوۀ اعلان هر کدام را بيان کنيد.- «تابع سازنده» را شناخته و وظيفۀ آن را شرح دهيد.- روشهاي گوناگون مقداردهي با استفاده از فهرست مقداردهي را بدانيد.- «تابع سازندۀ کپي» را معرفي کرده و وظيفۀ آن را شرح دهيد.- اعضاي «ايستا» را تعريف کرده و نحوۀ اعلان و استفاده از آنها را بدانيد.
اسلاید 466: «شيگرايي» رهيافت جديدي بود که براي پاره اي از مشکلات برنامه نويسي راه حل داشت. اين مضمون از دنياي فلسفه به جهان برنامهنويسي آمد و کمک کرد تا معضلات توليد و پشتيباني نرمافزار کمتر شود. اشيا را ميتوان با توجه به مشخصات ورفتار آنها دسته بندي کرد. مقدمه
اسلاید 467: در بحث شيگرايي به دستهها «کلاس» ميگويند و به نمونههاي هر کلاس «شي» گفته ميشود. مشخصات هر شي را «صفت» مينامند و به رفتارهاي هر شي «متد» ميگويند.
اسلاید 468: الف. بستهبندي: يعني اين که دادههاي مرتبط، با هم ترکيب شوند و جزييات پيادهسازي مخفي شود. برنامهنويسي شيگرا بر سه ستون استوار است:ب. وراثت: در دنياي واقعي، وراثت به اين معناست که يک شي وقتي متولد ميشود، خصوصيات و ويژگيهايي را از والد خود به همراه دارد .
اسلاید 469: امتياز وراثت در اين است که از کدهاي مشترک استفاده ميشود و علاوه بر اين که ميتوان از کدهاي قبلي استفاده مجدد کرد، در زمان نيز صرفهجويي شده و استحکام منطقي برنامه هم افزايش مييابد.
اسلاید 470: ج. چند ريختي: که به آن چندشکلي هم ميگويند به معناي يک چيز بودن و چند شکل داشتن است. چندريختي بيشتر در وراثت معنا پيدا ميکند.
اسلاید 471: اعلان كلاسها کد زير اعلان يک کلاس را نشان ميدهد. class Ratio{ public: void assign(int, int); viod print(); private: int num, den;};اعلان کلاس با کلمۀ کليدي class شروع ميشودسپس نام کلاس ميآيد.
اسلاید 472: عبارت public و عبارت private . هر عضوي که ذيل عبارت public اعلان شود، يک «عضو عمومي» محسوب ميشود و هر عضوي که ذيل عبارت private اعلان شود، يک «عضو خصوصي» محسوب ميشود. اعلان اعضاي کلاس درون يک بلوک انجام ميشود و سرانجام يک سميکولن بعد از بلوک نشان ميدهد که اعلان کلاس پايان يافته است.
اسلاید 473: سازندهها وظيفۀ تابع سازنده اين است که حافظۀ لازم را براي شيء جديد تخصيص داده و آن را مقداردهي نمايد و با اجراي وظايفي که در تابع سازنده منظور شده، شيء جديد را براي استفاده آماده کند.هر کلاس ميتواند چندين سازنده داشته باشد. در حقيقت تابع سازنده ميتواند چندشکلي داشته باشد.
اسلاید 474: سازندهها، از طريق فهرست پارامترهاي متفاوت از يکديگر تفکيک ميشوند. به مثال بعدي نگاه کنيد. class Ratio{ public: Ratio() { num = 0; den = 1; } Ratio(int n) { num = n; den = 1; } Ratio(int n, int d) { num = n; den = d; } void print() { cout << num << / << den; } private: int num, den;};مثال 5-9 افزودن چند تابع سازندۀ ديگر به كلاس Ratio
اسلاید 475: سومين سازنده نيز همان سازندۀ مثال 4-2 است.اين نسخه از كلاس Ratio سه سازنده دارد:اولي هيچ پارامتري ندارد و شيء اعلان شده را با مقدار پيشفرض 0 و 1 مقداردهي ميکند.دومين سازنده يک پارامتر از نوع int دارد و شيء اعلان شده را طوري مقداردهي ميکند که حاصل کسر با مقدار آن پارامتر برابر باشد.
اسلاید 476: يک کلاس ميتواند سازندههاي مختلفي داشته باشد. سادهترين آنها، سازندهاي است که هيچ پارامتري ندارد. به اين سازنده سازندۀ پيشفرض ميگويند. اگر در يک کلاس، سازندۀ پيشفرض ذکر نشود، کامپايلر به طور خودکار آن را براي کلاس مذکور ايجاد ميکند.
اسلاید 477: فهرست مقداردهي در سازندهها سازندهها اغلب به غير از مقداردهي دادههاي عضو يک شي، کار ديگري انجام نميدهند. به همين دليل در C++ يک واحد دستوري مخصوص پيشبيني شده که توليد سازنده را تسهيل مينمايد. اين واحد دستوري فهرست مقداردهي نام دارد.
اسلاید 478: به سومين سازنده در مثال 5-9 دقت کنيد. اين سازنده را ميتوانيم با استفاده از فهرست مقداردهي به شکل زير خلاصه کنيم:Ratio(int n, int d) : num(n), den(d) { }مثال 6-9 استفاده از فهرست مقداردهي در كلاس Ratio class Ratio{ public: Ratio() : num(0) , den(1) { } Ratio(int n) : num(n) , den(1) { } Ratio(int n, int d) : num(n), den(d) { } private: int num, den;};
اسلاید 479: توابع دستيابي دادههاي عضو يک کلاس معمولا به صورت خصوصي (private) اعلان ميشوند تا دستيابي به آنها محدود باشد اما همين امر باعث ميشود که نتوانيم در مواقع لزوم به اين دادهها دسترسي داشته باشيم. براي حل اين مشکل از توابعي با عنوان توابع دستيابي استفاده ميکنيم.
اسلاید 480: تابع دستيابي يک تابع عمومي عضو کلاس است و به همين دليل اجازۀ دسترسي به اعضاي دادهاي خصوصي را دارد. با استفاده از توابع دستيابي فقط ميتوان اعضاي دادهاي خصوصي را خواند ولي نميتوان آنها را دستکاري کرد.
اسلاید 481: مثال 8-9 افزودن توابع دستيابي به كلاس Ratioclass Ratio{ public: Ratio(int n=0, int d=1) : num(n) , den(d) { } int numerator() { return num; } int denomerator() { return den; } private: int num, den;};در اينجا توابع numerator() و denumerator() مقادير موجود در دادههاي عضو خصوصي را نشان ميدهند.
اسلاید 482: توابع عضو خصوصي توابع عضو را گاهي ميتوانيم به شکل يک عضو خصوصي کلاس معرفي کنيم. واضح است که چنين تابعي از داخل برنامۀ اصلي به هيچ عنوان قابل دستيابي نيست. اين تابع فقط ميتواند توسط ساير توابع عضو کلاس دستيابي شود. به چنين تابعي يک تابع سودمند محلي ميگوييم.
اسلاید 483: مثال 9-9 استفاده از توابع عضو خصوصيclass Ratio{ public: Ratio(int n=0, int d=1) : num(n), den(d) { } void print() { cout << num << / << den << endl; } void printconv() { cout << toFloat() << endl; } private: int num, den; double toFloat();};در برنامه زير، کلاس Ratio داراي يک تابع عضو خصوصي به نام toFloat() است. وظيفۀتابع مذکور اين است که معادل مميز شناور يک عدد کسري را برگرداند اين تابع فقط درون بدنۀ تابع عضو printconv() استفاده شده و به انجام وظيفۀ آن کمک مينمايد و هيچ نقشي در برنامۀ اصلي ندارد.
اسلاید 484: سازندۀ كپيميدانيم که به دو شيوه ميتوانيم متغير جديدي تعريف نماييم:int x;int x=k;در روش اول متغيري به نام x از نوع int ايجاد ميشود. در روش دوم هم همين کار انجام ميگيرد با اين تفاوت که پس از ايجاد x مقدار موجود در متغير k که از قبل وجود داشته درون x کپي ميشود. اصطلاحا x يک کپي از k است.
اسلاید 485: Ratio y(x); کد بالا يک شي به نام y از نوع Ratio ايجاد ميکند و تمام مشخصات شيء x را درون آن قرار ميدهد. اگر در تعريف کلاس، سازندۀ کپي ذکر نشود (مثل همۀ کلاسهاي قبلي) به طور خودکار يک سازندۀ کپي پيشفرض به کلاس افزوده خواهد شد.
اسلاید 486: مثال 10-9 افزودن يك سازندۀ كپي به كلاس Ratio{ public: Ratio(int n=0, int d=1) : num(n), den(d) { } Ratio(const Ratio& r) : num(r.num), den(r.den) { } void print() { cout << num << / << den; } private: int num, den;}; در مثال بالا، تابع سازندۀ کپي طوري تعريف شده که عنصرهاي num و den از پارامتر r به درون عنصرهاي متناظر در شيء جديد کپي شوند.
اسلاید 487: سازندۀ کپي در سه وضعيت فرا خوانده ميشود:1 – وقتي که يک شي هنگام اعلان از روي شيء ديگر کپي شود.2 – وقتي که يک شي به وسيلۀ مقدار به يک تابع ارسال شود.3 – وقتي که يک شي به وسيلۀ مقدار از يک تابع بازگشت داده شود .
اسلاید 488: نابود کننده وقتي که يک شي ايجاد ميشود، تابع سازنده به طور خودکار براي ساختن آن فراخواني ميشود. وقتي که شي به پايان زندگياش برسد، تابع عضو ديگري به طور خودکار فراخواني ميشود تا نابودکردن آن شي را مديريت کند. اين تابع عضو، نابودکننده ناميده ميشود .سازنده وظيفه دارد تا منابع لازم را براي شي تخصيص دهد و نابودکننده وظيفه دارد آن منابع را آزاد کند.هر کلاس فقط يک نابودکننده دارد.
اسلاید 489: مثال 12-9 افزودن يك نابودكننده به كلاس Ratioclass Ratio{ public: Ratio() { cout << OBJECT IS BORN.n; } ~Ratio() { cout << OBJECT DIES.n; } private: int num, den;};
اسلاید 490: اشياي ثابت اگر قرار است شيئي بسازيد که در طول اجراي برنامه هيچگاه تغيير نميکند، بهتر است منطقي رفتار کنيد و آن شي را به شکل ثابت اعلان نماييد. اعلانهاي زير چند ثابت آشنا را نشان ميدهند:const char BLANK = ;const int MAX_INT = 2147483647;const double PI = 3.141592653589793;void int(float a[], const int SIZE);اشيا را نيز ميتوان با استفاده از عبارت const به صورت يک شيء ثابت اعلان کرد:const Ratio PI(22,7);
اسلاید 491: اشارهگر به اشيا ميتوانيم اشارهگر به اشياي کلاس نيز داشته باشيم. از آنجا که يک کلاس ميتواند اشياي دادهاي متنوع و متفاوتي داشته باشد، اشارهگر به اشيا بسيار سودمند و مفيد است. اشارهگر به اشيا براي ساختن فهرستهاي پيوندي و درختهاي دادهاي به کار ميرود.
اسلاید 492: مثال 13-9 استفاده از اشارهگر به اشياclass X{ public: int data;};main(){ X* p = new X; (*p).data = 22; // equivalent to: p->data = 22; cout << (*p).data = << (*p).data << = << p->data << endl; p->data = 44; cout << p->data = << (*p).data << = << p->data << endl;}
اسلاید 493: در اين مثال، p اشارهگري به شيء x است. پس *p يک شيء x است و (*p).data دادۀ عضو آن شي را دستيابي ميکند. حتما بايد هنگام استفاده از *p آن را درون پرانتز قرار دهيد زيرا عملگر انتخاب عضو (.) تقدم بالاتري نسبت به عملگر مقداريابي (*) دارد. اگر پرانتزها قيد نشوند و فقط *p.data نوشته شود، کامپايلر اين خط را به صورت *(p.data) تفسير خواهد کرد که اين باعث خطا ميشود.
اسلاید 494: مثال بعدي اهميت بيشتري دارد و کاربرد اشارهگر به اشيا را بهتر نشان ميدهد. مثال 14-9 فهرستهاي پيوندي با استفاده از كلاس Node به کلاسي که در زير اعلان شده دقت کنيد:class Node{ public: Node(int d, Node* p=0) : data(d), next(p) { } int data; Node* next;};
اسلاید 495: عبارت بالا کلاسي به نام Node تعريف ميکند که اشياي اين کلاس داراي دو عضو دادهاي هستند که يکي متغيري از نوع int است و ديگري يک اشارهگر از نوع همين کلاس. اين کار واقعا ممکن است و باعث ميشود بتوانيم يک شي را با استفاده از همين اشارهگر به شيء ديگر پيوند دهيم و يک زنجيره بسازيم.
اسلاید 496: int dataqNode* nextint datarint datasNode* nextNode* next اگر اشياي q و r و s از نوع Node باشند، ميتوانيم پيوند اين سه شي را به صورت زير مجسم کنيم:
اسلاید 497: به تابع سازنده نيز دقت کنيد که چطور هر دو عضو دادهاي شيء جديد را مقداردهي ميکند. int main(){ int n; Node* p; Node* q=0; while (cin >> n) { p = new Node(n, q); q = p; } for ( ; p->next; p = p->next) cout << p->data << -> ; cout << *n;}
اسلاید 498: شکل زير روند اجراي برنامه را نشان ميدهد.int dataqNode* nextint dataqNode* nextint dataqNode* nextint datapNode* nextint dataNode* nextint datapNode* nextالف - قبل از شروع حلقهب - پس از اولين تکرار حلقهج - پس از دومين تکرار حلقه
اسلاید 499: اعضاي دادهاي ايستا هر وقت که شيئي از روي يک کلاس ساخته ميشود، آن شي مستقل از اشياي ديگر، دادههاي عضو خاص خودش را دارد. گاهي لازم است که مقدار يک عضو دادهاي در همۀ اشيا يکسان باشد. اگر اين عضو مفروض در همۀ اشيا تکرار شود، هم از کارايي برنامه ميکاهد و هم حافظه را تلف ميکند. در چنين مواقعي بهتر است آن عضو را به عنوان يک عضو ايستا اعلان کنيم.
اسلاید 500: عضو ايستا عضوي است که فقط يک نمونه از آن ايجاد ميشود و همه اشيا از همان نمونۀ مشترک استفاده ميکنند. با استفاده از کلمۀ کليدي static در شروع اعلان متغير، ميتوانيم آن متغير را به صورت ايستا اعلان نماييم. يک متغير ايستا را فقط بايد به طور مستقيم و مستقل از اشيا مقداردهي نمود. کد زير نحوۀ اعلان و مقداردهي يک عضو دادهاي ايستا را بيان ميکند:class X{ public: static int n; // declaration of n as a static data member};int X::n = 0; // definition of nخط آخر نشان ميدهد که متغيرهاي ايستا را بايد به طور مستقيم و مستقل از اشيا مقداردهي کرد.
اسلاید 501: متغيرهاي ايستا به طور پيشفرض با صفر مقداردهي اوليه ميشوند. بنابراين مقداردهي صريح به اين گونه متغيرها ضروري نيست مگر اين که بخواهيد يک مقدار اوليۀ غير صفر داشته باشيد.مثال 15-9 يك عضو دادهاي ايستاکد زير، کلاسي به نام widget اعلان ميکند که اين کلاس يک عضو دادهاي ايستا به نام count دارد. اين عضو، تعداد اشياي widget که موجود هستند را نگه ميدارد. هر وقت که يک شيء widget ساخته ميشود، از طريق سازنده مقدار count يک واحد افزايش مييابد و هر زمان که يک شيء widget نابود ميشود، از طريق نابودکننده مقدار count يک واحد کاهش مييابد:
اسلاید 502: class Widget{ public: Widget() { ++count; } ~Widget() { --count; } static int count;};int Widget::count = 0;main(){ Widget w, x; cout << Now there are << w.count << widgets.n; { Widget w, x, y, z; cout << Now there are << w.count << widgets.n; } cout << Now there are << w.count << widgets.n; Widget y; cout << Now there are << w.count << widgets.n;}Now there are 2 widgets.Now there are 6 widgets.Now there are 2 widgets.Now there are 3 widgets.
اسلاید 503: توجه کنيد که چگونه چهار شيء widget درون بلوک داخلي ايجاد شده است. هنگامي که اجراي برنامه از آن بلوک خارج ميشود، اين اشيا نابود ميشوند و لذا تعداد کل widgetها از 6 به 2 تقليل مييابد.يک عضو دادهاي ايستا مثل يک متغير معمولي است: فقط يک نمونه از آن موجود است بدون توجه به اين که چه تعداد شي از آن کلاس موجود باشد. از آنجا که عضو دادهاي ايستا عضوي از کلاس است، ميتوانيم آن را به شکل يک عضو خصوصي نيز اعلان کنيم.
اسلاید 504: * مثال 16-9 يك عضو دادهاي ايستا و خصوصيclass Widget{ public: Widget() { ++count; } ~Widget() { --count; } int numWidgets() { return count; } private: static int count;};
اسلاید 505: int Widget::count = 0;main(){ Widget w, x; cout << Now there are << w.numWidgets() << widgets.n; { Widget w, x, y, z; cout << Now there are << w.numWidgets() << widgets.n; } cout << Now there are << w.numWidgets() << widgets.n; Widget y; cout << Now there are << w.numWidgets() << widgets.n;}
اسلاید 506: اين برنامه مانند مثال 15-9 کار ميکند با اين تفاوت که متغير ايستاي count به شکل يک عضو خصوصي اعلان شده و به همين دليل به تابع دستيابي numWidgets() نياز داريم تا بتوانيم درون برنامۀ اصلي به متغير count دسترسي داشته باشيم. ميتوانيم کلاس Widget و اشياي x و y و w را مانند مقابل تصور کنيم:Widget()~Widget()numWidgets()count3Widgetxyw
اسلاید 507: 12- توابع عضو ايستابا دقت در مثال قبلي به دو ايراد بر ميخوريم: اول اين که گرچه متغير count يک عضو ايستا است ولي براي خواندن آن حتما بايد از يک شيء موجود استفاده کنيم. در مثال قبلي از شيء w براي خواندن آن استفاده کردهايم. اين باعث ميشود که مجبور شويم هميشه مواظب باشيم عضو ايستاي مفروض از طريق يک شي که الان موجود است فراخواني شود.
اسلاید 508: * مثال 17-9 يك تابع عضو ايستاکد زير همان کد مثال قبلي است با اين فرق که در اين کد، تابع دستيابي کننده نيز به شکل ايستا اعلان شده است:class Widget{ public: Widget() { ++count; } ~Widget() { --count; } static int num() { return count; } private: static int count;};
اسلاید 509: int Widget::count = 0;int main(){ cout << Now there are << Widget::num() << widgets.n; Widget w, x; cout << Now there are << Widget::num() << widgets.n; { Widget w, x, y, z; cout << Now there are << Widget::num() << widgets.n; } cout << Now there are << Widget::num() << widgets.n; Widget y; cout << Now there are << Widget::num() << widgets.n;}
اسلاید 510: وقتي تابع num() به صورت ايستا تعريف شود، از اشياي کلاس مستقل ميشود و براي فراخواني آن نيازي به يک شيء موجود نيست و ميتوان با کد Widget::num() به شکل مستقيم آن را فراخواني کرد.
اسلاید 511: پايان جلسه نهم
اسلاید 512: جلسه دهم«سربارگذاري عملگرها »
اسلاید 513: 1- توابع دوست2- سربارگذاري عملگر جايگزيني (=)3- اشارهگر this4- سربارگذاري عملگرهاي حسابي5- سربارگذاري عملگرهاي جايگزيني حسابي6- سربارگذاري عملگرهاي رابطهاي7- سربارگذاري عملگرهاي افزايشي و كاهشيآنچه در اين جلسه مي خوانيد:
اسلاید 514: هدف کلي: بيان اهميت سربارگذاري عملگرها براي يک کلاس و نحوۀ انجام اين کار.
اسلاید 515: هدفهاي رفتاري: انتظار ميرود پس از پايان اين جلسه بتوانيد:- «سربارگذاري» را تعريف کرده و اهميت آن را شرح دهيد.- «تابع دوست» را تعريف کنيد و علت و اهميت استفاده از چنين توابعي را بيان نماييد.- اشارهگر this را بشناسيد و علت استفاده از چنين اشارهگري را بيان نماييد.- نحوۀ سربارگذاري عملگر جايگزيني را بيان کنيد.- نحوۀ سربارگذاري عملگرهاي حسابي را بيان کنيد.- نحوۀ سربارگذاري عملگرهاي جايگزيني حسابي را بيان کنيد.- نحوۀ سربارگذاري عملگرهاي رابطهاي را بيان کنيد.- نحوۀ سربارگذاري عملگرهاي افزايشي و کاهشي را بيان کنيد.
اسلاید 516: مقدمه:در C++ مجموعهاي از 45 عملگر مختلف وجود دارد که براي کارهاي متنوعي استفاده ميشوند. همۀ اين عملگرها براي کار کردن با انواع بنيادي (مثل int و float و char) سازگاري دارند. هنگامي که کلاسي را تعريف ميکنيم، در حقيقت يک نوع جديد را به انواع موجود اضافه کردهايم. ممکن است بخواهيم اشياي اين کلاس را در محاسبات رياضي به کار ببريم. اما چون عملگرهاي رياضي (مثل + يا = يا *= ) چيزي راجع به اشياي کلاس جديد نميدانند، نميتوانند به درستي کار کنند. C++ براي رفع اين مشکل چاره انديشيده و امکان سربارگذاري عملگرها را تدارک ديده است. سربارگذاري عملگرها به اين معناست که به عملگرها تعاريف جديدي اضافه کنيم تا بتوانند با اشياي کلاس مورد نظر به درستي کار کنند.
اسلاید 517: class Ratio{ friend int numReturn(Ratio); public: Ratio(); ~Ratio(); private: int num, den;}int numReturn(Ratio r){ return r.num; }int main(){ Ratio x(22, 7);1 – Friend function cout << numReturn(x) << endl;} 1- توابع دوست: اعضايي از کلاس که به شکل خصوصي (private) اعلان ميشوند فقط از داخل همان کلاس قابل دستيابياند و از بيرون کلاس (درون بدنۀ اصلي) امکان دسترسي به آنها نيست. اما يک استثنا وجود دارد. تابع دوست تابعي است که عضو يک کلاس نيست اما اجازه دارد به اعضاي خصوصي آن دسترسي داشته باشد. به کد زير نگاه کنيد:
اسلاید 518: در بين عملگرهاي گوناگون، عملگر جايگزيني شايد بيشترين کاربرد را داشته باشد. هدف اين عملگر، کپي کردن يک شي در شيء ديگر است. مانند سازندۀ پيشفرض، سازندۀ کپي و نابودکننده، عملگر جايگزيني نيز به طور خودکار براي يک کلاس ايجاد ميشود اما اين تابع را ميتوانيم به شکل صريح درون کلاس اعلان نماييم.2-سربارگذاري عملگر جايگزيني(=):
اسلاید 519: class Ratio{ public: Ratio(int = 0, int = 1); Ratio(const Ratio&); void operator=(const Ratio&); private: int num, den;};مثال: افزودن عملگر جايگزيني به كلاس: کد زير يک رابط کلاس براي Ratio است که شامل سازندۀ پيشفرض، سازندۀ کپي و عملگر جايگزيني ميباشد:
اسلاید 520: به نحو اعلان عملگر جايگزيني دقت نماييد. نام اين تابع عضو، operator= است و فهرست آرگومان آن مانند سازندۀ کپي ميباشد يعني يک آرگومان منفرد دارد که از نوع همان کلاس است که به طريقۀ ارجاع ثابت ارسال ميشود. عملگر جايگزيني را ميتوانيم به شکل زير تعريف کنيم: void Ratio::operator=(const Ratio& r){ num = r.num; den = r.den;}کد فوق اعضاي دادهاي شيء r را به درون اعضاي دادهاي شيئي که مالک فراخواني اين عملگر است، کپي ميکند.
اسلاید 521: در C++ ميتوانيم عملگر جايگزيني را به شکل زنجيرهاي مثل زير به کار ببريم: x = y = z = 3.14;3-اشارهگر :this اجراي کد بالا از راست به چپ صورت ميگيرد. يعني ابتدا مقدار 3.14 درون z قرار ميگيرد و سپس مقدار z درون y کپي ميشود و سرانجام مقدار y درون x قرار داده ميشود. عملگر جايگزيني که در مثال قبل ذکر شد، نميتواند به شکل زنجيرهاي به کار رود.
اسلاید 522: مثال 2-10 سربارگذاري عملگر جايگزيني به شکل صحيح:class Ratio{ public: Ratio(int =0, int =1); // default constructor Ratio(const Ratio&); // copy constructor Ratio& operator=(const Ratio&); // assignment operator // other declarations go here private: int num, den; // other declarations go here};Ratio& Ratio::operator=(const Ratio& r){ num = r.num; den = r.den; return *this;}
اسلاید 523: توجه داشته باشيد که عمل جايگزيني با عمل مقداردهي تفاوت دارد، هر چند هر دو از عملگر يکساني استفاده ميکنند. مثلا در کد زير:Ratio x(22,7); // this is an initializationRatio y(x); // this is an initializationRatio z = x; // this is an initializationRatio w;w = x; // this is an assignmentسه دستور اول، دستورات مقداردهي هستند ولي دستور آخر يک دستور جايگزيني است. دستور مقداردهي، سازندۀ کپي را فرا ميخواند ولي دستور جايگزيني عملگر جايگزيني را فراخواني ميکند.
اسلاید 524: 4-سربارگذاري عملگرهاي حسابي:چهار عملگر حسابي + و – و * و / در همۀ زبانهاي برنامهنويسي وجود دارند و با همۀ انواع بنيادي به کار گرفته ميشوند. قصد داريم سرباري را به اين عملگرها اضافه کنيم تا بتوانيم با استفاده از آنها، اشياي ساخت خودمان را در محاسبات رياضي به کار ببريم.عملگرهاي حسابي به دو عملوند نياز دارند. مثلا عملگر ضرب (*) در رابطۀ زير:z = x*y;با توجه به رابطۀ فوق و آنچه در بخش قبلي گفتيم، عملگر ضرب سربارگذاري شده بايد دو پارامتر از نوع يک کلاس و به طريق ارجاع ثابت بگيرد و يک مقدار بازگشتي از نوع همان کلاس داشته باشد. پس انتظار داريم قالب سربارگذاري عملگر ضرب براي کلاس Ratio به شکل زير باشد:Ratio operator*(Ratio x, Ratio y){ Ratio z(x.num*y.num, x.den*y.den); return z;}
اسلاید 525: اگر تابعي عضو کلاس نباشد، نميتواند به اعضاي خصوصي آن کلاس دستيابد. براي رفع اين محدوديتها، تابع سربارگذاري عملگر ضرب را بايد به عنوان تابع دوست کلاس معرفي کنيم. لذا قالب کلي براي سربارگذاري عملگر ضرب درون کلاس مفروض T به شکل زير است:Class T{ friend T operator*(const T&, const T&); public: // public members private: // private members}
اسلاید 526: و از آنجا که تابع دوست عضوي از کلاس نيست، تعريف بدنۀ آن بايد خارج از کلاس صورت پذيرد. در تعريف بدنۀ تابع دوست به کلمۀ کليدي friend نيازي نيست و عملگر جداسازي حوزه :: نيز استفاده نميشود:T operator*(const T& x, const T& y){ T z; // required operations for z = x*y return z;}در سربارگذاري عملگرهاي حسابي + و – و / نيز از قالبهاي کلي فوق استفاده ميکنيم با اين تفاوت که در نام تابع سربارگذاري، به جاي علامت ضرب * بايد علامت عملگر مربوطه را قرار دهيم و دستورات بدنۀ تابع را نيز طبق نياز تغيير دهيم.
اسلاید 527: class Ratio{ friend Ratio operator*(const Ratio&, const Ratio&); public: Ratio(int = 0, int = 1); Ratio(const Ratio&); Ratio& operator=(const Ratio&); // other declarations go here private: int num, den; // other declarations go here};Ratio operator*(const Ratio& x, const Ratio& y){ Ratio z(x.num * y.num , x.den * y.den); return z;}int main(){ Ratio x(22,7) ,y(-3,8) ,z; z = x; // assignment operator is called z.print(); cout << endl; x = y*z; // multiplication operator is called x.print(); cout << endl;}مثال 3-10 سربارگذاري عملگر ضرب براي کلاس :Ratio
اسلاید 528: class T{ public: T& operator*=(const T&); // other public members private: // private members};5-سربارگذاري عملگرهاي جايگزيني حسابي:به خاطر بياوريد که عملگرهاي جايگزيني حسابي، ترکيبي از عملگر جايگزيني و يک عملگر حسابي ديگر است. مثلا عملگر *= ترکيبي از دو عمل ضرب * و سپس جايگزيني = است. نکتۀ قابل توجه در عملگرهاي جايگزيني حسابي اين است که اين عملگرها بر خلاف عملگرهاي حسابي ساده، فقط يک عملوند دارند. پس تابع سربارگذاري عملگرهاي جايگزيني حسابي بر خلاف عملگرهاي حسابي، ميتواند عضو کلاس باشد. سربارگذاري عملگرهاي جايگزيني حسابي بسيار شبيه سربارگذاري عملگر جايگزيني است. قالب کلي براي سربارگذاري عملگر *= براي کلاس مفروض T به صورت زير است:
اسلاید 529: استفاده از اشارهگر *this باعث ميشود که بتوانيم عملگر *= را در يک رابطۀ زنجيرهاي به کار ببريم. در C++ چهار عملگر جايگزيني حسابي += و -= و *= و /= وجود دارد. قالب کلي براي سربارگذاري همۀ اين عملگرها به شکل قالب بالا است فقط در نام تابع به جاي *= بايد علامت عملگر مربوطه را ذکر کرد و دستورات بدنۀ تابع را نيز به تناسب، تغيير داد. مثال بعدي نشان ميدهد که عملگر *= چگونه براي کلاس Ratio سربارگذاري شده است.بدنۀ تابع سربارگذاري به قالب زير است:T& T::operator*=(const T& x){ // required operations return *this;}
اسلاید 530: class Ratio{ public: Ratio(int = 0, int = 1); Ratio& operator=(const Ratio&); Ratio& operator*=(const Ratio&); // other declarations go here private: int num, den; // other declarations go here};Ratio& Ratio::operator*=(const Ratio& r){ num = num*r.num; den = den*r.den; return *this;}مثال 4-10 كلاس Ratio با عملگر *= سربارگذاري شده:بديهي است که عملگر سربارگذاري شدۀ جايگزيني حسابي، بايد با عملگر سربارگذاري شدۀ حسابي معادلش، نتيجۀ يکساني داشته باشد. مثلا اگر x و y هر دو از کلاس Ratio باشند، آنگاه دو خط کد زير بايد نتيجۀ مشابهي داشته باشند:x *= y;x = x*y;
اسلاید 531: شش عملگر رابطهاي در C++ وجود دارد که عبارتند از: > و < و => و <= و == و != . اين عملگرها به همان روش عملگرهاي حسابي،يعني به شکل توابع دوست سربارگذاري ميشوند. اما نوع بازگشتيشان فرق ميکند. 6-سربارگذاري عملگرهاي رابطهاي:
اسلاید 532: چون نوع بولين در حقيقت يک نوع عددي صحيح است، ميتوان به جاي true مقدار 1 و به جاي false مقدار 0 را قرار داد. به همين جهت نوع بازگشتي را براي توابع سربارگذاري عملگرهاي رابطهاي، از نوع int قرار دادهاند. حاصل عبارتي که شامل عملگر رابطهاي باشد، همواره يک مقدار بولين است. يعني اگر آن عبارت درست باشد، حاصل true است و اگر آن عبارت نادرست باشد، حاصل false است.
اسلاید 533: قالب کلي براي سربارگذاري عملگر رابطهاي == به شکل زير است:class T{ friend int operator==(const T&, const T&); public: // public members private: // private members}
اسلاید 534: همچنين قالب کلي تعريف بدنۀ اين تابع به صورت زير ميباشد:int operator==(const T& x,const T& y){ // required operations to finding result return result;}که به جاي result يک مقدار بولين يا يک عدد صحيح قرار ميگيرد. ساير عملگرهاي رابطهاي نيز از قالب بالا پيروي ميکنند.
اسلاید 535: class Ratio{ friend int operator==(const Ratio&, const Ratio&); frined Ratio operator*(const Ratio&, const Ratio&); // other declarations go here public: Ratio(int = 0, int = 1); Ratio(const Ratio&); Ratio& operator=(const Ratio&); // other declarations go here private: int num, den; // other declarations go here};int operator==(const Ratio& x, const Ratio& y){ return (x.num * y.den == y.num * x.den);}چون اشياي کلاس Ratio به صورت کسر هستند، بررسي تساوي x==y معادل بررسي است که براي بررسي اين تساوي ميتوانيم مقدار (a*d==b*c) را بررسي کنيم. بدنۀ تابع سربارگذاري در مثال همين رابطه را بررسي ميکند.مثال 5-10 سربارگذاري عملگر تساوي (==) براي كلاس :Ratio
اسلاید 536: 7-سربارگذاري عملگرهاي افزايشي و كاهشي:عملگر افزايشي ++ و کاهشي -- هر کدام دو شکل دارند:1- شکل پيشوندي. 2-شکل پسوندي. هر کدام از اين حالتها را ميتوان سربارگذاري کرد.
اسلاید 537: قالب کلي براي سربارگذاري عملگر پيشافزايشي به شکل زير است:T T::operator++(){ // required operations return *this;}اينجا هم از اشارهگر *this استفاده شده. علت هم اين است که مشخص نيست چه چيزي بايد بازگشت داده شود. به همين دليل اشارهگر *this به کار رفته تا شيئي که عمل پيشافزايش روي آن صورت گرفته، بازگشت داده شود.
اسلاید 538: افزودن عملگر پيشافزايشي به كلاس :Ratioاگر y يک شي از کلاس Ratio باشد و عبارت ++y ارزيابي گردد، مقدار 1 به y افزوده ميشود اما چون y يک عدد کسري است، افزودن مقدار 1 به اين کسر اثر متفاوتي دارد. فرض کنيد y=22/7 باشد. حالا داريم:
اسلاید 539: افزودن عملگر پسافزايشي به كلاس Ratio:در عبارت y = x++; از عملگر پسافزايشي استفاده کردهايم. تابع اين عملگر بايد طوري تعريف شود که مقدار x را قبل از اين که درون y قرار بگيرد، تغيير ندهد. ميدانيم که اشارهگر *this به شيء جاري (مالک فراخواني) اشاره دارد. کافي است مقدار اين اشارهگر را در يک محل موقتي ذخيره کنيم و عمل افزايش را روي آن مقدار موقتي انجام داده و حاصل آن را بازگشت دهيم. به اين ترتيب مقدار *this تغييري نميکند و پس از شرکت در عمل جايگزيني، درون y قرار ميگيرد: class Ratio{ public: Ratio(int n=0, int d=1) : num(n) , den(d) { } Ratio operator++(); //pre-increment Ratio operator++(int); //post-increment void print() { cout << num << / << den << endl; } private: int num, den;};int main(){ Ratio x(22,7) , y = x++; cout << y = ; y.print(); cout << , x = ; x.print();}Ratio Ratio::operator++(int){ Ratio temp = *this; num += den; return temp;}
اسلاید 540: عملگرهاي پيشکاهشي و پسکاهشي نيز به همين شيوۀ عملگرهاي پيشافزايشي و پسافزايشي سربارگذاري ميشوند. غير از اينها، عملگرهاي ديگري نيز مثل عملگر خروجي (<<) ، عملگر ورودي (>>) ، عملگر انديس ([]) و عملگر تبديل نيز وجود دارند که ميتوان آنها را براي سازگاري براي کلاسهاي جديد سربارگذاري کرد.
اسلاید 541: پايان جلسه دهم
اسلاید 542: جلسه يازدهم «تركيب و وراثت»
اسلاید 543: مقدمه تركيب وراثت اعضاي حفاظت شد غلبه کردن بر وراثت اشارهگرها در وراثتتوابع مجازي و چندريختي نابودكنندۀ مجازي <<<«تركيب و وراثت»
اسلاید 544: كلاسهاي پايۀ انتزاعي پرسشهاي گزينهاي پرسشهاي تشريحيتمرينهاي برنامهنويسيضميمه الف : پاسخنامۀ پرسشهاي گزينهاي ضميمه ب:جدول اسکيضميمه ج : کلمات کليدي C++ استاندارد ضميمه د : عملگرهاي C++ استاندارد ضميمه هـ : فهرست منابع و مأخذ
اسلاید 545: هدف کلي: بيان اهميت ترکيب و وراثت در شيگرايي و چگونگي انجام اين کارها.هدفهاي رفتاري: انتظار ميرود پس از پايان اين جلسه بتوانيد:- علت استفاده از «ترکيب» و «وراثت» را در برنامههاي شيگرا توضيح دهيد.- نحوۀ ترکيب دو يا چند کلاس را براي ايجاد کلاس جديد، بدانيد.- وراثت را تعريف کنيد.
اسلاید 546: - «اعضاي حفاظت شدۀ کلاس» را تعريف کنيد و تفاوت اين اعضا با اعضاي عمومي و خصوصي کلاس را شرح دهيد.- نحوۀ غلبه کردن بر وراثت را شرح دهيد.- «تابع مجازي» را تعريف کنيد و علت استفاده از توابع مجازي را بدانيد.- «چندريختي» را تعريف کنيد و شيوۀ پيادهسازي چندريختي در کلاسها را بدانيد.- «کلاس پايۀ انتزاعي» را تعريف کنيد و علت تعريف اين کلاسها را ذکر کنيد.
اسلاید 547: مقدمهاغلب اوقات براي ايجاد يک کلاس جديد، نيازي نيست که همه چيز از اول طراحي شود. ميتوانيم براي ايجاد کلاس مورد نظر، از تعاريف کلاسهايي که قبلا ساختهايم، استفاده نماييم.اين باعث صرفهجويي در وقت و استحکام منطق برنامه ميشود. در شيگرايي به دو شيوه ميتوان اين کار را انجام داد: ترکيب1 و وراثت2. در اين جلسه خواهيم ديد که چگونه و چه مواقعي ميتوانيم از اين دو شيوه بهره ببريم.
اسلاید 548: تركيب: ترکيب کلاسها (يا تجميع کلاسها) يعني استفاده از يک يا چند کلاس ديگر در داخل تعريف يک کلاس جديد. هنگامي که عضو دادهاي کلاس جديد، شيئي از کلاس ديگر باشد، ميگوييم که اين کلاس جديد ترکيبي از ساير کلاسهاست. به تعريف دو کلاس زير نگاه کنيد.
اسلاید 549: كلاس Date کد زير، کلاس Date را نشان ميدهد که اشياي اين کلاس براي نگهداري تاريخ استفاده ميشوند.class Date{ public: Date(int y=0, int m=0, int d=0) : year(y), month(m), day(d) {}; void setDate(int y, int m, int d) { year = y; month = m; day = d; } void getDate() { cin >> year >> month >> day ; } void showDate() { cout << year << / << month << / << day ; } private: int year, month, day;}
اسلاید 550: كلاس Book کد زير، کلاس Book را نشان ميدهد که اشياي اين کلاس برخي از مشخصات يک کتاب را نگهداري ميکنند:class Book{ public: Book(char* n = , int i = 0, int p = 0) : name(n), id(i), page(p) { } void printName() { cout << name; } void printId() { cout << id; } void printPage() { cout << page; } private: string name, author; int id, page;}
اسلاید 551: بهبود دادن کلاس Book #include Date.hclass Book{ public: Book(char* n = , int i = 0, int p = 0) : name(n), id(i), page(p) { } void printName() { cout << name; } void printId() { cout << id; } void printPage() { cout << page; } void setDOP(int y, int m, int d) { publish.setDate(y, m, d) ; } void showDOP() { publish.showDate(); } private: string name, author; int id, page; Date publish;}
اسلاید 552: وراثت :وراثت روش ديگري براي ايجاد کلاس جديد از روي کلاس قبلي است. گاهي به وراثت «اشتقاق» نيز ميگويند.اگر از قبل با برنامهنويسي مبتني بر پنجرهها آشنايي مختصر داشته باشيد، احتمالا عبارت «کلاس مشتقشده» را فراوان ديدهايد. اين موضوع به خوبي اهميت وراثت را آشکار مينمايد.
اسلاید 553: اعضاي حفاظت شده: گرچه کلاس Ebook در مثال قبل نميتواند مستقيما به اعضاي خصوصي کلاس والدش دسترسي داشته باشد، اما با استفاده از توابع عضو عمومي که از کلاس والد به ارث برده، ميتواند به اعضاي خصوصي آن کلاس دستيابي کند. اين محدوديت بزرگي محسوب ميشود. اگر توابع عضو عمومي کلاس والد انتظارات کلاس فرزند را برآورده نسازند، کلاس فرزند ناکارآمد ميشود. اوضاع زماني وخيمتر ميشود که هيچ تابع عمومي براي دسترسي به يک دادۀ خصوصي در کلاس والد وجود نداشته باشد.
اسلاید 554: غلبه کردن بر وراثت :اگر Y زير کلاسي از X باشد، آنگاه اشياي Y همۀ اعضاي عمومي و حفاظت شدۀ کلاس X را ارث ميبرند. مثلا تمامي اشياي Ebook تابع دستيابي printName() از کلاس Book را به ارث ميبرند. به تابع printName() يک «عضو موروثي» ميگوييم. گاهي لازم است يک نسخۀ محلي از عضو موروثي داشته باشيم. يعني کلاس فرزند، عضوي هم نام با عضو موروثي داشته باشد که مخصوص به خودش باشد و ارثي نباشد. براي مثال فرض کنيد کلاس X يک عضو عمومي به نام p داشته باشد و کلاس Y زير کلاس X باشد.
اسلاید 555: در اين حالت اشياي کلاس Y عضو موروثي p را خواهند داشت. حال اگر يک عضو به همان نام p در زيرکلاس Y به شکل صريح اعلان کنيم، اين عضو جديد، عضو موروثي همنامش را مغلوب ميکند. به اين عضو جديد، «عضو غالب» ميگوييم. بنابراين اگر y1 يک شي از کلاس Y باشد، y1.p به عضو p غالب اشاره دارد نه به p موروثي. البته هنوز هم ميتوان به p موروثي دسترسي داشت. عبارت y1.X::p به p موروثي دستيابي دارد.
اسلاید 556: هم ميتوان اعضاي دادهاي موروثي را مغلوب کرد و هم اعضاي تابعي موروثي را. يعني اگر کلاس X داراي يک عضو تابعي عمومي به نام f() باشد و در زيرکلاس Y نيز تابع f() را به شکل صريح اعلان کنيم، آنگاه y1.f() به تابع غالب اشاره دارد و y1.X::f() به تابع موروثي اشاره دارد. در برخي از مراجع به توابع غالب override ميگويند و دادههاي غالب را dominate مينامند. ما در اين کتاب هر دو مفهوم را به عنوان اعضاي غالب به کار ميبريم. به مثال زير نگاه کنيد.
اسلاید 557: اعضاي دادهاي و تابعي غالب :class X{ public: void f() { cout << Now X::f() is runningn; } int a;};class Y : public X{ public: void f() { cout << Now Y::f() is runningn; } // this f() overrides X::f() int a; // this a dominates X::a};
اسلاید 558: سازندهها و نابودکنندههاي والد:class X{ public: X() { cout << X::X() constructor executingn; } ~X() { cout << X::X() destructor executingn; }};clas Y : public X{ public: Y() { cout << Y::Y() constructor executingn; } ~Y() { cout << Y::Y() destructor executingn; }};clas Z : public Y{ public: Z(int n) {cout << Z::Z(int) constructor executingn;} ~Z() { cout << Z::Z() destructor executingn; }};int main(){ Z z(44);}
اسلاید 559: اشارهگرها در وراثت :در شيگرايي خاصيت جالبي وجود دارد و آن اين است که اگر p اشارهگري از نوع کلاس والد باشد، آنگاه p را ميتوان به هر فرزندي از آن کلاس نيز اشاره داد. به کد زير نگاه کنيد:class X{ public: void f();} class Y : public X // Y is a subclass of X{ public: void f();}int main(){ X* p; // p is a pointer to objects of base class X Y y; p = &y; // p can also point to objects of subclass Y}
اسلاید 560: اشارهگري از کلاس والد به شيئي از کلاس فرزند: در برنامۀ زير، کلاس Y زيرکلاسي از X است. هر دوي اين کلاسها داراي يک عضو تابعي به نام f() هستند و p اشارهگري از نوع X* تعريف شده:class X{ public: void f() { cout << X::f() executingn; }};class Y : public X{ public: void f() { cout << Y::f() executingn; }}int main(){ X x; Y y; X* p = &x; p->f(); // invokes X::f() because p has type X* p = &y; p->f(); // invokes X::f() because p has type X*}
اسلاید 561: توابع مجازي و چندريختي :تابع مجازي تابعي است که با کلمۀ کليدي virtual مشخص ميشود. وقتي يک تابع به شکل مجازي اعلان ميشود، يعني در حداقل يکي از کلاسهاي فرزند نيز تابعي با همين نام وجود دارد. توابع مجازي امکان ميدهند که هنگام استفاده از اشارهگرها، بتوانيم بدون در نظر گرفتن نوع اشارهگر، به توابع شيء جاري دستيابي کنيم. به مثال زير دقت کنيد.
اسلاید 562: استفاده از توابع مجازي: class X{ public:1 – Virtual function virtual void f() { cout << X::f() executingn; }};class Y : public X{ public: void f() { cout << Y::f() executingn; }}int main(){ X x; Y y; X* p = &x; p->f(); // invokes X::f() p = &y; p->f(); // invokes Y::f()}
اسلاید 563: چندريختي از طريق توابع مجازي :سه کلاس زير را در نظر بگيريد. بدون استفاده از توابعمجازي، برنامه آن طور که مورد انتظار است کار نميکند:class Book{ public: Book(char* s) { name = new char[strlen(s+1)]; strcpy(name, s); } void print() { cout << Here is a book with name << name << .n; } protected: char* name;};class Ebook : public Book{ public: Ebook(char* s, float g) : Book(s), size(g) {} void print() { cout << Here is an Ebook with name << name << and size << size << MB.n; } private: float size;}class Notebook : public Book{ public: Notebook(char* s, int n) : Book(s) , pages(n) {} void print() { cout << Here is a Notebook with name << name << and << pages << pages.n; } private: int pages;};
اسلاید 564: نابودكنندۀ مجازي: با توجه به تعريف توابع مجازي، به نظر ميرسد که نميتوان توابع سازنده و نابودکننده را به شکل مجازي تعريف نمود زيرا سازندهها و نابودگرها در کلاسهاي والد و فرزند، همنام نيستند. در اصل، سازندهها را نميتوان به شکل مجازي تعريف کرد اما نابودگرها قصۀ ديگري دارند.مثال بعدي ايراد مهلکي را نشان ميدهد که با مجازي کردن نابودگر، برطرف ميشود.
اسلاید 565: حافظۀ گم شده :به برنامۀ زير دقت کنيد:class X{ public: x() { p = new int[2]; cout << X(). ; } ~X() { delete [] p; cout << ~X().n } private: int* p;};class Y : public X{ public: Y() { q = new int[1023]; cout << Y() : Y::q = << q << . ; } ~Y() { delete [] q; cout << ~Y(). ; } private: int* q;};int main(){ for (int i=0; i<8; i++) { X* r = new Y; delete r; }}
اسلاید 566: كلاسهاي پايۀ انتزاعي :در شيگرايي رسم بر اين است که ساختار برنامه و کلاسها را طوري طراحي کنند که بتوان آنها را به شکل يک نمودار درختي شبيه زير نشان داد:BOOKPaper BOOKEBOOKREFERENCEMAGAZINECOOKBOOKPDFCHMHLPHTML
خرید پاورپوینت توسط کلیه کارتهای شتاب امکانپذیر است و بلافاصله پس از خرید، لینک دانلود پاورپوینت در اختیار شما قرار خواهد گرفت.
در صورت عدم رضایت سفارش برگشت و وجه به حساب شما برگشت داده خواهد شد.
در صورت بروز هر گونه مشکل به شماره 09353405883 در ایتا پیام دهید یا با ای دی poshtibani_ppt_ir در تلگرام ارتباط بگیرید.
- پاورپوینتهای مشابه
نقد و بررسی ها
هیچ نظری برای این پاورپوینت نوشته نشده است.