یکی از نویسندهگان این کتاب، پورفسور H.M.Deitel شاهد یک فروپاشی در اواخر دهه ۱۹۶۰ توسط سازمانهای توسعه نرمافزار، بویژه در شرکتهای بود که پروژههائی در سطح کلان انجام میدادند. زمانیکه Deitel دانشجو بود، فرصت کار کردن در یک گروه توسعه دهنده در بخش اشتراک زمانی و حافظه مجازی سیستمهای عامل را بدست آورد، که در نوع خود یک تجربه با ارزش محسوب میشد. اما در تابستان ۱۹۶۷، شرکت تصمیم به متوقف کردن پروژه بدلایل اقتصادی کرد، پروژهای که صدها نفر بمدت چندین سال بر روی آن کار کرده بودند. این موضوع نشان داد که نرمافزار محصول پیچیدهای است. بهبود وضعیت نرمافزار از زمانی آغاز شد که مزایای برنامهنویسی ساختیافته بر همگان هویدا گردید (از دهه ۹۷۰ ). اما تا دهه ۱۹۹۰ تکنولوژی برنامهنویسی شیگرا بطرز گستردهای بکار گرفته نشد، تا اینکه برنامهنویسان به وجود ابزاری برای بهبود فرآیند توسعه نرمافزار، احساس نیاز کردند. در واقع، پیدایش تکنولوژی شی به میانه دهه ۱۹۶۰ باز میگردد. زبان برنامهنویسی C++ در AT&T و توسط Bjarne Stroustrup در اوایل دهه ۱۹۸۰ توسعه یافته که خود مبتنی بر دو زبان C و Simula 67 است. زبان C در بدو کار در AT&T و برای پیاده سازی سیستم عامل یونیکس در اوایل دهه ۱۹۷۰ ایجاد شد. زبان Simula 67، زبان برنامهنویسی شبیه سازی است که در سال ۱۹۶۷ و در اروپا توسعه یافته است. زبان C++ در برگیرنده قابلیتها و توانائی زبانهای C و Simula در ایجاد و دستکاری شیها است. شیها چیستند و چرا خاص میباشند؟ در واقع، تکنولوژی شی، یک الگوی بسته (package) است که به ما در ایجاد واحدهای نرمافزاری با معنی کمک میکند. شیها تمرکز زیادی بر نواحی خاص برنامهها دارند. شیها میتوانند از جمله شیهای تاریخ، زمان، پرداخت، فاکتور، صدا، ویدئو، فایل، رکورد و بسیاری از موارد دیگر باشد. در حقیقت، میتوان هر چیزی را بفرم یک شی عرضه کرد. ما در دنیای از شیها زندگی میکنیم. کافیست نگاهی به اطراف خود بیاندازیم. اطراف ما پر است از اتومبیلها، هواپیما، انسانها، حیوانات، ساختمانها، چراغهای ترافیگ، بالابرها، و بسیاری از چیزهای دیگر. قبل از اینکه زبانهای برنامهنویسی شیگرا ابداع شوند، زبانهای برنامهنویسی ( همانند FORTRAN، Pascal ، C و Basic ) بر روی اعمال یا actions بجای چیزها یا شیها (نام) تمرکز داشتند. با اینکه برنامهنویسان در دنیائی از شیها زندگی میکردند اما با فعلها سرگرم بودند. خود همین تناقض باعث شد تا برنامههای نوشته شده از قدرت کافی برخوردار نباشند. هم اکنون که زبانهای برنامهنویسی شیگرا همانند C و جاوا در دسترس هستند، برنامهنویسان به زندگی خود در یک دنیای شیگرا ادامه میدهند و میتوانند برنامههای خود را با اسلوب شیگرا بنویسند. فرآیند برنامهنویسی شیگرا در مقایسه با برنامهنویسی روالی (procedural) ماهیت بسیار طبیعیتری دارد و نتیجه آن هم رضایت بخشتر است. اصلیترین مشکل برنامهنویسی روالی این است که واحدهای تشکیل دهنده برنامه نمیتوانند به آسانی نشاندهنده موجودیتهای دنیای واقعی باشند. از اینرو، این واحدها نمیتوانند بطرز شایستهای از قابلیت استفاده مجدد برخوردار باشند. برای برنامه نویسان روالی، نوشتن دوباره و مجدد کدها در هر پروژه جدید و با وجود مشابه بودن این کدها با نرمافزار پروژههای قبلی، چندان غیر عادی نمیباشد. نتیجه اینکار تلف شدن زمان و هزینهها است. با تکنولوژی شی، موجودیتهای نرمافزاری (کلاس) ایجاد میشوند و در صورتیکه بدرستی طراحی شده باشند، میتوان در آینده از آنها در پروژههای دیگر استفاده کرد. استفاده از کتابخانه کامپونتها با قابلیت بکارگیری مجدد همانند MFC(Microsoft Foundation Classes) و کامپونتهای تولید شده توسط Rogue Wave و سایر شرکتهای نرمافزاری، میتواند در کاهش هزینهها و نیازهای پیادهسازی سیستمهای خاص بسیار موثر باشد. با بکارگیری برنامهنویسی شیگرا، نرمافزار تولید شده بسیار قابل فهمتر شده، نگهداری و سازماندهی آن اصولیتر و اصلاح و خطایابی آن سادهتر میشود. این موارد از اهمیت خاصی برخوردار هستند چراکه تخمین زده میشود که هشتاد در صد هزینه یک نرمافزار مربوط به دوره نگهداری و ارتقاء آن در چرخه طول عمرش است و ارتباطی با نوشتن و توسعه اولیه نرمافزار ندارد. با تمام این اوصاف، مشخص است که برنامهنویسی شیگرا تبدیل به یکی از کلیدیترین مفاهیم برنامهنویسی در چند دهه آینده خواهد شد. یکی از دلایل نوشتن کدهای متعلق بخود این است که دقیقا میدانید که کد نوشته شده چگونه کار میکند، و میتوانید به بررسی آن بپردازید. عیب اینکار هم در صرف زمان و پیچیدگی طراحی و پیادهسازی کد جدید است. ۱۴-۱ محیط توسعه C++ اجازه دهید تا به بررسی مراحل ایجاد و اجرای یک برنامه کاربردی C++ با استفاده از محیط توسعه C++ بپردازیم (شکل ۱-۱). اصولاً سیستم C++ متشکل از سه بخش است: محیط توسعه برنامه, زبان و کتابخانه استاندارد C++. عموماً برنامههای C++ از شش فاز یا مرحله عبور میکنند: ویرایش, پیش پردازش، کامپایل, لینک, بار شدن و اجرا. در ادامه به توضیح هر فاز میپردازیم. فاز ۱: ایجاد برنامه فاز ۱ مرکب از ویرایش یک فایل با استفاده از یک برنامه ویرایشگر است. با استفاده از یک ویرایشگر مبادرت به تایپ برنامه C++ کرده و هرگونه اصلاحات مورد نیاز را در آن اعمال و بر روی یک دستگاه ذخیرهسازی ثانویه, همانند دیسک سخت ذخیره میکنیم. فایلهای برنامه C++ دارای پسوندهای .cpp , .cxx , .cc یا .C میباشند (دقت کنید که C با حرف بزرگ است), که نشان میدهند, که فایل حاوی کد منبع C++ است. برای مشاهده پسوند فایل در محیط توسعه C++ به مستندات محیط توسعه C++ بکار رفته بر روی سیستم خود مراجعه کنید. شکل ۱-۱ | . دو ویرایشگر که در سیستم UNIX کاربرد بیشتری دارند عبارتند از vi و emacs. بستههای نرمافزاری C++ برای ویندوز میکروسافت همانند Metroworks CodeWarrior com), Borland C++ ( و Microsoft Visual C++ ( دارای ویرایشگر مجتمع در محیط برنامهنویسی خود هستند. البته میتوانید از یک ویرایشگر متنی ساده همانند Notepad در ویندوز, برای نوشتن کد C++ استفاده کنید. فرض ما بر این است که شما حداقل با نحوه ویرایش یک برنامه آشنا هستید. فاز ۲ و ۳: پیش پردازنده و کامپایل یک برنامه C++ در فاز ۲, برنامهنویس, دستوری برای کامپایل برنامه صادر میکند. در یک سیستم C++, برنامه پیشپردازنده, قبل از اینکه فاز ترجمه کامپایلر شروع بکار کند, آغاز یا اجرا میشود (از اینرو به پیشپردازنده فاز ۲ و به کامپایل فاز ۳ نام گذاشتهایم). پیشپردازنده فرمانبر دستوراتی بنام رهنمود پیشپردازنده است که عملیات مشخص بر روی برنامه قبل از کامپایل آن انجام میدهند. معمولاً این عملیات شامل کامپایل سایر فایلهای متنی و انجام انواع جایگزینیها است. در فصلهای آتی با تعدادی از رهنمودهای پیشپردازنده آشنا خواهید شد. در فاز ۳, کامپایلر شروع به ترجمه برنامه C++ به کد زبان ماشین میکند که گاهاً بعنوان کد شی (object code) شناخته میشود. فاز ۴: لینک فاز ۴ معروف به فاز لینک است. معمولاً برنامههای C++ حاوی مراجعههایی به توابع و دادههای تعریف شده در بخشهای گوناگون هستند, همانند کتابخانههای استاندارد یا کتابخانههای خصوصی گروهی از برنامهنویسان که بر روی یک پروژه کار میکنند. کد شی تولید شده توسط کامپایلر C++ حاوی شکافهایی است که از فقدان این بخشها بوجود میآیند. برنامه لینکر مبادرت به لینک کد شی با کد توابع مفقوده میکند تا یک تصویر کامل و بدون شکاف بوجود آید. اگر برنامه بدرستی کامپایل و لینک گردد, یک نماد اجرایی تولید خواهد شد. فاز ۵: بار شدن فاز ۵, فاز بار شدن نام دارد. قبل از اینکه برنامه بتواند اجرا شود, ابتدا بایستی در حافظه جای گیرد. این کار توسط بارکننده (loader) صورت میگیرد که نماد اجرایی را از دیسک دریافت و به حافظه منتقل میکند. همچنین برخی از کامپونتها که از برنامه پشتیبانی میکنند, به حافظه بار میشوند. فاز ۶: اجرا سرانجام, کامپیوتر, تحت کنترل CPU خود, مبادرت به اجرای یک به یک دستورالعملهای برنامه میکند. مسائلی که ممکن است در زمان اجرا رخ دهند همیشه برنامهها در همان بار اول اجرا نمیشوند. در هر کدامیک از فازهای فوق امکان شکست وجود دارد، چرا که ممکن است انواع مختلفی از خطاها که در مورد آنها صحبت خواهیم کرد, در این بین رخ دهند. برای مثال, امکان دارد برنامه اقدام به انجام عملیات تقسیم بر صفر نماید (یک عملیات غیرمعتبر). در چنین حالتی برنامه C++ یک پیغام خطا بنمایش در خواهد آورد. اگر چنین شود, مجبور هستید تا به فاز ویرایش بازگردید و اصلاحات لازم را انجام داده و مجدداً مابقی فازها را برای تعیین اینکه آیا اصلاحات صورت گرفته قادر به رفع مشکل بودهاند یا خیر, طی کنید. بیشتر برنامههای نوشته شده در C++ عملیات ورود و یا خروج داده انجام میدهند. توابع مشخصی از C++, از طریق cin (استریم ورودی استاندارد, با تلفظ “see-in”) که معمولاً از طریق صفحهکلید است, اقدام به دریافت ورودی میکنند, اما تابع cin میتواند به دستگاه دیگری هدایت شود. غالباً دادهها از طریق دستور cout (استریم استاندارد خروجی, با تلفظ “see-out”) خارج میگردند که معمولاً صفحهنمایش کامپیوتر است, اما میتوان cout را به دستگاه دیگری هدایت کرد. زمانیکه میگوییم برنامه نتیجهای را چاپ میکند, معمولاً منظورمان نمایش نتیجه بر روی صفحه نمایش (مانیتور) است. میتوان دادهها را به دستگاههای دیگری همانند دیسکها ارسال کرد و توسط چاپگر از آنها چاپ گرفت. همچنین دستوری بنام cerr (استریم استاندارد خطا) وجود دارد, که از آن برای نمایش پیغامهای خطا استفاده میشود, این دستور اکثراً برای نمایش پیغام در صفحه نمایش بکار گرفته میشود. ۱۵-۱ نکاتی در مورد C++ و این کتاب گاهی اوقات برنامهنویسان با تجربه C++ بدون رعایت هیچگونه ساختار و روش مشخص شروع به برنامهنویسی میکنند, که چنین کاری در برنامهنویسی روش ضعیفی است. خواندن, نگهداری, تست و خطایابی چنین برنامههایی بسیار مشکل بوده و گاهاً رفتار عجیبی از خود بروز میدهند. این کتاب با در نظر گرفتن برنامهنویسان مبتدی سعی در نوشتن واضح برنامهها دارد.
پاسخ : آموزش ++C مفاهیم پایه تکنولوژی شی مقدمه بحث شیگرا را با برخی اصطلاحات کلیدی آغاز میکنیم. به هر کجا در دنیای واقعی نگاه کنید. شاهد شیها خواهید بود. مردم، حیوانات، گیاهان، اتومبیلها، هواپیماها، ساختمانها، کامپیوترها و بسیاری از چیزهای دیگر. تفکر انسان براساس شی است. تلفنها، خانهها، چراغهای ترافیک، اجاقهای میکروویو و کولرهای آبی تعدادی کمی از بیشمار شیها هستند که در زندگی روزمره خود شاهد آنها هستیم. گاهی اوقات مبادرت به تقسیم شیها به دو طبقه میکنیم: متحرک و غیرمتحرک. شیهای متحرک، زنده هستند و میتوانند حرکت کرده و کاری انجام دهند. از سوی دیگر، شیهای غیرمتحرک، در محیط خود حرکت نمیکنند. با این همه، شیها از هر دو نوع، در برخی چیزها مشترک هستند. همه آنها دارای صفات (همانند سایز، شکل، رنگ و وزن) بوده و از خود رفتارهای نشان میدهند (مثلاً توپ میغلتد، بالا و پایین میپرد، یک بچه گریه میکند، میخوابد، راه میرود، یک اتومبیل شتاب میگیرد، ترمز کرده و چرخش میکند، یک حوله آب را جذب میکند). انسانها با بررسی صفات و رفتار اشیاء، مطالبی در ارتباط با آنها یاد میگیرند. شیهای مختلف میتوانند صفات و رفتار مشابهی داشته باشند. مقایسه را همیشه میتوان اعمال کرد، مثلاً مابین کودکان و بزرگسالان. طراحی شیگرا یک روش طبیعی و ذاتی در نمایش فرآیند طراحی نرمافزار بنام مدلسازی شیها توسط صفات، رفتار و رابطه موجود در میان شیها همانند شیها در دنیای واقعی است. همچنین OOD ارتباط مابین شیها را مدلسازی میکند. همانند فردی که پیغامی به شخص دیگری میفرستد، شیها نیز از طریق پیغام ارتباط برقرار میکنند. یک شی حساب بانکی میتواند پیغامی برای کاهش موجودی دریافت کند چرا که مشتری مقداری از پول خود را از حساب برداشت کرده است. OOD مبادرت به کپسولهسازی صفات و عملیات (رفتار) در شیها میکند. سرانجام صفات و رفتار یک شی با هم گره میخورند. شیها از خصیصه پنهانسازی اطلاعات برخوردار هستند. به این معنی که شیها از نحوه برقراری ارتباط با یک شی دیگر از طریق یک واسط (رابط) مناسب مطلع هستند، اما معمولاٌ از نحوه پیادهسازی شیهای دیگر اطلاعی ندارند. معمولاٌ جزئیات پیادهسازی در درون خود شیها پنهان میباشند. میتوان این امر را با رانندگی اتومبیل مقایسه کرد که در آن برای راندن اتومبیل نیازی نیست تا با جزئیات موتور، جعبهدنده و عملکرد ترمز آشنا بود. در ادامه با نحوه پنهانسازی اطلاعات و دلایل آن آشنا خواهید شد، که از جمله موارد مناسب در مهندسی نرمافزار است. زبانهای همانند C++ شیگرا هستند. به برنامهنویسی در چنین زبانهای، برنامهنویسی شیگرا (OOP) گفته میشود و به برنامهنویسان کامپیوتری امکان میدهند تا یک طرح شیگرا را بصورت یک سیستم نرمافزاری پیادهسازی کنند. از سوی دیگر زبانهای همانند C، زبانهای روالی هستند و از اینرو برنامهنویسان مقید به عمل میباشند. در C واحد برنامهنویسی تابع است، در حالیکه در C++ این واحد، کلاس میباشد. کلاسهای C++ حاوی توابعی هستند که پیادهسازیکننده صفات میباشند. برنامهنویسان C بر روی نوشتن توابع تمرکز دارند. آنها گروهی از عملها را که وظایفی انجام میدهند در درون یک تابع قرار میدهند و سپس گروهی از توابع را به فرم یک برنامه در میآورند. بطور مشخص دادهها در C بسیار با ارزش هستند، اما از همان ابتدای امر برای پشتیبانی از اعمالی که توابع انجام خواهند داد وجود دارند. کلاسها، اعضا داده و توابع عضو تمرکز برنامهنویسان C++ بر روی ایجاد «نوعهای تعریف شده از سوی کاربر» که کلاس نامیده میشوند است. هر کلاس حاوی داده و هم مجموعهای از متدها است که بر روی دادهها کار میکنند و سرویسهای برای سرویسگیرندهها (کلاسها و توابع دیگری که از کلاس استفاده میکنند) تدارک میبیند، به کامپونتهای داده از یک کلاس اعضای داده گفته میشود. برای مثال، یک کلاس حساب بانکی میتواند شامل یک شماره حساب و یک موجودی باشد. به کامپونتهای داده از یک کلاس، توابع عضو گفته میشود (معمولاٌ در سایر زبانهای برنامهنویسی شیگرا همانند جاوا، به توابع عضو، متد گفته میشود). برای مثال، یک کلاس حساب بانکی میتواند دارای توابع عضوی برای ایجاد یک پسانداز (افزایشدهنده موجودی)، برداشت (کاهشدهنده موجودی) و نمایش موجودی فعلی باشد. برنامهنویس از نوعهای توکار (و سایر نوعهای تعریف شده توسط کاربر) بعنوان «بلوکهای سازنده» در ایجاد نوعهای جدید (کلاسها) استفاده میکند. اسامی در ساختار یک سیستم به برنامهنویس C++ در تعیین اینکه کدام شیها برای کار با یکدیگر طراحی شدهآند کمک میکنند. کلاسها همانند نقشه ترسیمی خانهها هستند. یک کلاس، نقشه ایجاد یک شی از کلاس است. همانطوری که میتوانیم خانههای متعددی از روی یک نقشه بسازیم، میتوانیم تعدادی شی از روی یک کلاس، نمونهسازی کنیم. نمیتوان در نقشه آشپزخانه مبادرت به آشپزی کرد، آشپزی فقط در آشپزخانه خانه امکانپذیر است. کلاسها میتوانند با کلاسهای دیگر ارتباط داشته باشند. برای مثال در یک طرح شیگرا از بانک، کلاس “bank teller” نیاز به برقراری ارتباط با کلاسهای دیگر همانند کلاس «مشتری»، کلاس «پرداخت نقدی»، کلاس «پسانداز» و غیره دارد. به این روابط، وابستگی گفته میشود. بسته کردن (package) نرمافزار بصورت کلاسها میتواند ویژگی استفاده مجدد از نرمافزار را عرضه کند. غالباً کلاسهای مرتبط با یکدیگر را بصورت کامپونتهای با قابلیت استفاده مجدد بستهبندی میکنند. طراحی نرمافزار مدلهای شیگرا OOD (Object-oriented design) شبیه به روشی است که افراد برای توصیف شیها در دنیای واقعی بکار میبرند. میتوان از رابطه موجود مابین کلاس استفاده کرد، آنجا که شیهایی از یک کلاس همانند کلاس وسیله نقلیه دارای ویژگیهای یکسان هستند، اتومبیلها، تریلیها دارای صفات مشترک میباشند. OOD از مزیت ارثبری (یا توارث) سود میبرد، که در آن کلاسهای جدید، صفاتی را از کلاسهای موجود جذب میکنند و به صفات خاص خود اضافه مینمایند. مقدمهای بر تحلیل و طراحی شیگرا (OOAD) بزودی شروع به برنامهنویسی در C++ خواهید کرد. چگونه میخواهید کد برنامههای خود را ایجاد کنید؟ شاید، میخواهید همانند هر برنامهنویس تازهکاری، فقط کامپیوتر را روشن کرده و شروع به تایپ برنامه کنید. این روش میتواند در ارتباط با برنامههای کوچک کاربرد داشته باشد، اما اگر از شما خواسته شود تا یک سیستم نرمافزاری برای کنترل صدها دستگاه ماشین پرداخت اتوماتیک یک بانک ایجاد کنید، چه خواهید کرد؟ یا اگر از شما خواسته شود تا با یک تیم هزار نفری در ارتباط با تولید نسل بعدی سیستم کنترل ترافیک هوایی ایالات متحده کار کنید، چه خواهید کرد؟ برای پروژههای به این بزرگی و پیچیدگی، نمیتوانید به راحتی پشت کامپیوتر نشسته و شروع به برنامهنویسی کنید. برای دستیابی به بهترین راه حل، بایستی بدقت نیازمندیهای پروژه را تجزیه و تحلیل کنید (تعیین کنید که سیستم چه کار میخواهد انجام دهد) و طرحی را توسعه دهید که آنها را برآورده سازد (سیستم قادر به تصمیمگیری صحیح برای انجام وظایف خود باشد). در حالت ایدهآل، قبل از هرگونه کدنویسی، باید به سراغ فرآیند رفته و به دقت طراحی را انجام دهید. اگر این فرآیند مستلزم تحلیل و طراحی سیستم از نقطه نظر شیگرا باشد، آن را object-orientend analysis and design (OOAD) یا تحلیل و طراحی شیگرا میگویند. برنامهنویسان با تجربه مطلع هستند که تحلیل و طراحی میتواند در زمان و هزینه ایجاد برنامه بسیار صرفهجویی کند، با اجتناب از اعمال طرحهای ضعیف که در هر بار برنامه را به شکست کشانده و باعث میشوند کار از ابتدا آغاز گردد که همان تحمیل هزینه و زمان است. OOAD یک کلمه کلی برای بیان فرآیند تجزیه و تحلیل یک مسئله و توسعه روش حل آن مسئله است. مسائل کوچکی همانند مسائلی که در چند فصل اولیه با آنها برخورد خواهیم داشت، نیازی به فرآیند OOAD ندارند. برای ایجاد این برنامهها، میتوان ابتدا مبادرت به نوشتن شبه کد آنها کرد و سپس کد C++ آنها را نوشت. در فصل ۴ به معرفی شبه کد خواهیم پرداخت. با بزرگتر و پیچیدهتر شدن مسئله، برتری روش OOAD به روش شبه کد کاملاٌ مشخص میشود. اگر چه پردازشهای مختلفی از OOAD وجود دارد، اما یک زبان گرافیکی برای نمایش نتایج هر فرآیند OOAD بیشتر از همه بکار گرفته شده است. این زبان UML (Unified Modeling Language) نام دارد که اواسط دهه ۱۹۹۰ تحت نظر سه نظریهپرداز نرمافزار بنامهای Grady Booch، James Rumbaugh و Ivar Jacobson توسعه پیدا کرده است. تاریخچه UML در دهه ۱۹۸۰، تعداد سازمانهایی که شروع به استفاده از OOP برای ایجاد برنامههای کاربردی خود کردند بسیار افزایش یافت و از اینرو نیاز به یک استاندارد OOAD احساس کردید. تعدادی از نظریهپردازان شامل Booch، Rumbaugh و Jacobson، بطور جداگانه مبادرت به ایجاد و عرضه فرآیندهای برای برطرف کردن این نیاز کردند. هر فرآیند یا پردازه دارای نمادها یا «زبان» (به فرم دیاگرامهای گرافیکی) متعلق بخود بود، تا حاصل نتایج تجزیه و تحلیل و طراحیها باشد. در اوایل دهه ۱۹۹۰، سازمانهای مختلف و حتی قسمتهای موجود در درون یک سازمان، در حال استفاده از فرآیندهای منحصر بفرد با نمادهای متعلق بخود بودند. همزمان، این سازمانها مایل به استفاده از ابزارهای نرمافزاری بودند که از فرآیندهای خاص آنها پشتیبانی کند. سازندگان نرمافزار متوجه مشکل تهیه ابزارهایی برای کار با این همه فرآیند متفاوت شدند. واضح بود که نیاز به یک نماد و فرآیند استاندارد بوجود آمده است. در سال ۱۹۹۴، James Rumbaugh با همکاری Grady Booch در شرکت Rational Softwave (که اکنون بخشی از IBM است)، شروع به متحدالشکل کردن فرآیندهای خود کرد. بلافاصله Ivar Jacobson نیز به آنها پیوست. در سال ۱۹۹۶، گروه مبادرت به عرضه نسخه اولیه از UML به جامعه مهندسین نرمافزار کرد و پاسخ آنها را خواستار شدند. در همان زمان، سازمانی که بعنوان OMG (Object Management Group) شناخته میشود، دعوت به یک زبان مدلسازی مشترک از گروههای مختلف کرد. OMG () یک سازمان غیرتجاری است که مبادرت به نشر تکنولوژیهای استاندارد شیگرا همانند UML میکند. چندین شرکت، از جمله HP، IBM، Microsoft، Oracle و Rational Software تشخیص داده بودند که نیاز به یک زبان مدلسازی مشترک است. در پاسخ به تقاضای OMG، این شرکتها، همکاری UML خود را آغاز کردند، کنسرسیوم UML نسخه ۱٫۱ را عرضه و تحویل OMG داد. OMG آن را پذیرفته و در۱۹۹۷، مسئولیت ادامه و نگهداری UML را قبول کرد. در مارس ۲۰۰۳، OMG مبادرت به عرضه UML نسخه ۱٫۵ کرد. UML نسخه ۲ که در زمان نشر این کتاب مراحل پایانی خود را میگذارند، اصلیترین نسخه از سال ۱۹۹۷ است. از اینرو بحث ما بر روی UML نسخه ۲ خواهد بود. UML چیست؟ در حال حاضر زبان UML یکی از پرکاربردترین طرحهای نمایش گرافیکی برای مدل کردن سیستمهای شیگرا است. این زبان انواع فرآیندهای موجود را متحد کرده است. برای مدل کردن سیستمها از این زبان در سراسر کتاب استفاده کردهایم. یکی از ویژگیهای جذاب UML در انعطافپذیری آن است. UML بسطپذیر بوده (ویژگیهای جدید را میپذیرد) و از هر فرآیند یا طریقه خاص OOAD مستقل است. مدلکنندههای UML برای استفاده در طراحی سیستمها مجانی هستند. UML یک زبان مرکب با ویژگیهای گرافیکی مناسب است. در بخشهای «مبحث آموزشی مهندسی نرمافزار» در توسعه نرمافزار ماشین پرداخت اتوماتیک (ATM) نمایش ساده و زیرمجموعهای از ویژگیتهای UML عرضه خواهیم کرد. این مباحث آموزشی به دقت و تحت نظر اساتید دانشگاه و افراد حرفهای طراحی شدهاند. امیدواریم که از مطالعه و کار کردن با این مباحث لذت ببرید.
پاسخ : آموزش ++C مقدمه در این فصل به معرفی برنامهنویسی C++ میپردازیم، که طراحی برنامهها را تسهیل خواهد بخشید. اکثر برنامههای که در این کتاب با آنها مواجه خواهید شد، مبادرت به پردازش اطلاعات و نمایش نتایج میکنند. در این فصل، به معرفی پنج مثال میپردازیم که نحوه نمایش پیغام و همچنین دریافت داده از کاربران را به شما نشان میدهند. سه مثال اول، ساده بوده و تنها مبادرت به نمایش پیغام در صفحه نمایش میکنند. برنامه بعدی، دو عدد از کاربر دریافت کرده و مجموع آنها را محاسبه و نتیجه را بنمایش درمیآورد. در ضمیمه بحث، شما را با نحوه انجام انواع محاسبات و ذخیره نتایج برای استفادههای بعدی آشنا خواهیم کرد. مثال پنجم در ارتباط با تصمیمسازی است که مبادرت به مقایسه دو عدد کرده و سپس پیغامی براساس مقایسه بنمایش درمیآورد. برای اینکه درک مناسب و آسانی از برنامهنویسی C++ بدست آورید، خط به خط هر برنامه را تحلیل میکنیم. برای اینکه مهارتهای کسب کرده خود از این فصل را بکار گیرید، چند مسئله برنامهنویسی در بخش تمرینات در نظر گرفتهایم. ۲-۲ یک برنامه ساده: چاپ یک عبارت متنی زبان C++ از نمادهای استفاده میکند که ممکن است برای غیر برنامهنویسان عجیب و غریب بنظر برسند. در این بخش به بررسی برنامهای میپردازیم که عبارتی را در یک خط چاپ میکند. برنامه و خروجی آن در شکل۱-۲ آورده شده است. این برنامه حاوی چندین ویژگی مهم زبان C++ است. برای آشنایی بهتر شما، به توضیح خط به خط برنامه میپردازیم. در خطوط ۱ و ۲ // Fig. 2.1: fig02_01.cpp // Text-printing program. که با //، آغاز شدهاند، نشاندهنده این مطلب هستند که این خطوط توضیح میباشند. برنامهنویسان توضیحات را در برنامه یا لیست کد قرار میدهند تا خوانایی کدهای خود را افزایش دهند. توضیحات میتوانند در خط متعلق به خود جای داده شوند که آنها را توضیحات تمام خط مینامیم یا در انتهای یک خط از کد که آنها را توضیحات انتهای خط مینامیم، قرار بگیرند. کامپایلرC++ توضیحات را نادیده میگیرد، به این معنی که توضیحات هیچ تأثیری در اجرای برنامه ندارند. توضیح بکار رفته در خط ۱ فقط نشاندهنده شماره تصویر و نام فایل این برنامه است. البته برخی از برنامهنویسان عبارات توضیحی خود را مابین کاراکترهای /* و */ قرار میدهند. شکل ۱-۲ | برنامه چاپ عبارت. int main() بخشی از هر برنامه C++ است. پرانتزهای واقع پس از main نشان میدهند که main یک بلوک برنامه بنام تابع است. برنامههای C++ میتوانند حاوی یک یا چندین تابع و کلاس باشند، اما بایستی یکی از آنها حتما main باشد، حتی اگر main اولین تابع در برنامه نباشد. کلمه کلیدی int که در سمت چپ main قرار گرفته، بر این نکته دلالت دارد که main یک مقدار صحیح “برمیگرداند” (عدد بدون اعشار). کلمهکلیدی، کلمهای در کد است که توسط C++ رزرو شده است. لیست کامل کلمات کلیدی C++ در جدول شکل ۳-۴ آورده شدهاند. به هنگام مطالعه فصل سوم و ششم به توضیح مفهوم دقیق “مقدار برگشتی” از سوی یک تابع خواهیم پرداخت. اما برای این لحظه، کافیست بدانید که کلمه کلیدی int در سمت چپ برنامههای شما قرار خواهد گرفت. بایستی براکت چپ، }، (خط ۷) در ابتدای بدنه هر روالی قرار داده شود. براکت متناظر، براکت راست، {، (خط ۱۲ ) است،که باید آنرا در انتهای بدنه هر روالی قرار داد. خط ۸ std::cout << “Welcome to C++!\n”; // display message به کامپیوتر فرمان میدهد تا رشتهای از کاراکترها را که مابین جفت گوتیشن قرار دارند بر روی صفحه نمایش چاپ کند. گاهی اوقات رشته بنام رشته کاراکتری، پیغام یا رشته لیترال هم خوانده میشود. کاراکترهای white space توسط کامپایلر نادیده گرفته میشوند. کل خط ۸، شامل std::cout، عملگر <<، رشته “Welcome to C++ ! \n” و یک سیمیکولن ( است، که به کل این خط یک عبارت گفته میشود. هر عبارتی در C++ باید با یک سیمیکولن خاتمه پذیرد (که به آن خاتمهدهنده عبارت هم گفته میشود). رهنمودهای پیشپردازنده (همانند #include) با سیمکولن خاتمه نمییابند. خروجی و ورودی در C++ با جریانی (stream) از کاراکترها پیادهسازی میشود. بنابر این، زمانیکه عبارت قبلی اجرا میشود، مبادرت به ارسال جریانی از کاراکترهای Welcome to C++ ! به شی جریان خروجی استاندارد (std::cout) که معمولا با صفحه نمایش مرتبط است میکند. در فصل پانزدهم به بررسی بیشتر std::cout خواهیم پرداخت. دقت کنید که std:: قبل از cout قرار گرفته است. انجام این عمل به هنگام استفاده از رهنمود پیشپردازنده #include <iostream> الزامی است. نماد std::cout نشان میدهد که در حال استفاده از یک نام هستیم، که این نام در این مورد cout میباشد، که متعلق به “فضاینامی” std است. فضاهاینامی از ویژگیهای پیشرفته C++ هستند. در فصل بیستوچهارم بطور کامل با فضاهاینامی آشنا خواهید شد. اما برای این لحظه، باید بخاطر داشته باشید که کلمه std:: را قبل از هر کدامیک از نمادهای cout، cin و cerr در یک برنامه قرار دهید. در برنامه شکل ۱۳-۲ از این نمادها به همراه عبارت using استفاده شده است، که ما را قادر به حذف std:: قبل از هر استفاده از فضای نامی std میکند. عملگر << نشاندهنده، عملگر درج جریان است. هنگامی که این برنامه اجرا میشود، مقدار موجود در سمت راست این عملگر، عملوند سمت راست، وارد جریان خروجی میگردد. دقت کنید که عملگر مستقیماٌ به مکانی اشاره میکند که داده باید به آنجا برود. معمولا کاراکترهای قرار گرفته در سمت راست عملگر به همان شکلی که مابین جفت گوتیشها آورده شدهاند، چاپ میشوند. با این وجود، توجه نمائید که کاراکتر \n بر روی صفحه نمایش چاپ نمیشود. کاراکتر\ کاراکتر escape نامیده میشود. این کاراکتر نشان میدهد که یک کاراکتر ویژه چاپ خواهد شد. زمانیکه در دنبالهای از رشتههای کاراکتری یک کاراکتر \ وارد شود، کاراکتر پس از آن بعنوان یک توالی escape در نظر گرفته خواهد شد. در این برنامه توالی escape کاراکتر \n است، که به مفهوم خط جدید یا newline میباشد. این کاراکتر سبب میشود تا کرسر به ابتدای خط بعدی در صفحه نمایش منتقل شود. در جدول شکل ۲-۲ تعدادی از توالیهای escape که از کاربرد بیشتری برخوردار هستند آورده شدهاند. توالی escape توضیحات \n خط جدید. کرسر را در ابتدای خط بعدی قرار میدهد. \t تب افقی. کرسر را به اندازه یک تب(tab) به جلو انتقال میدهد. \r enter. کرسر را به ابتدای خط جاری باز میگرداند. آنرا با \n اشتباه نگیرید. \a هشدار. زنگ یا صدای سیستم را فعال میکند. \\ کاراکتر \. برای چاپ کاراکتر \ استفاده میشود. \’ کاراکتر .’ برای چاپ کارکتر ‘ استفاده میشود. \” جفت گوتیشن. از این توالی برای چاپ کاراکتر جفت گوتیشن استفاده میشود شکل ۲-۲ |توالی escape. خط ۱۰ return 0; // indicate that program ended successfully در پایان هر تابع main وجود خواهد داشت. کلمه کلیدی return یکی از چندین روش موجود برای خروج از یک تابع است. زمانیکه عبارت return در پایان تابع main بکار گرفته میشود، همانند این برنامه، مقدار ۰ نشاندهنده این مطلب است که برنامه با موفقیت به کار خود پایان داده است. در فصل ششم، با جزئیات عملکرد توابع و دلایل افزودن چنین عباراتی به آنها بیشتر آشنا خواهید شد. اما برای این لحظه، کافیست بدانید که این عبارت در انتهای هر برنامهای قرار داده میشود. براکت راست، {، (خط ۱۲ ) انتهای تابع main را نشان میدهد. ۳-۲ اصلاح برنامه این بخش ادامهدهنده معرفی برنامهنویسی C++ با دو مثال است، که نحوه اصلاح برنامه مطرح شده در شکل ۱-۲ را نشان میدهند. در مثال اول، متن در یک خط با استفاده از چندین عبارت چاپ میشود. در مثال دوم همان متن توسط یک عبارت و در چندین خط چاپ میگردد. چاپ متن در یک خط توسط چند عبارت رشته Welcome to C++! میتواند به چندین روش چاپ شود. برای مثال، در برنامه شکل ۳-۲ از دو عبارت برای وارد کردن جریان (خطوط ۸-۹ )استفاده شده است، تا همان خروجی برنامه شکل ۱-۲ تولید شود. خروجی برنامه دوم دقیقاٌ همانند برنامه اول است، چرا که هر عبارت از مکانی که عبارت قبلی به عمل چاپ پایان داده، شروع به چاپ میکند. عبارت اول کلمه Welcome و بدنبال آن یک فاصله چاپ کرده و عبارت دوم هم کار چاپ را از همان خط و پس از فاصله چاپ شده دنبال میکند. بطور کلی، C++ به برنامهنویس امکان میدهد تا عبارات را به روشهای گوناگون بکار گیرد. چاپ متن در چند خط توسط یک عبارت همانند برنامه شکل ۴-۲ میتوان یک عبارت را در چندین خط به نمایش در آورد. هر بار که عبارت ارسالی به خروجی با کاراکتر توالی \n مواجه شود، کرسر به ابتدای خط بعدی منتقل خواهد شد. برای ایجاد یک خط خالی در خروجی، دو کاراکتر خط جدید را پشت سرهم، همانند خط ۸ قرار دهید. شکل ۳-۲ | چاپ متن در یک خط با چند عبارت. شکل ۴-۲ | چاپ متن در چند خط با یک عبارت.
پاسخ : آموزش ++C یک برنامه ساده دیگر: جمع اعداد صحیح در برنامه بعدی از شی جریان ورودی std::cin و عملگر استخراج >>، برای بدست آوردن دو عدد صحیح تایپ شده از سوی کاربر از طریق صفحهکلید، جمع آنها و نمایش نتیجه بدست آمده با استفاده از std::cout میپردازیم. برنامه شکل ۵-۲ عمل جمع و خروجی حاصل از این برنامه را نشان میدهد. شکل ۵-۲ | برنامه جمع که مجموع دو عدد صحیح دریافتی از صفحه کلید را محاسبه میکند. توضیحات موجود در خطوط ۱ و ۲ // Fig. 2.5: fig02_05.cpp // Addition program that displays the sum of two numbers. نشاندهنده نام فایل و هدف برنامه هستند. رهنمود پیشپردازنده C++ #include <iostream> // allows program to perform input and output در خط ۳ حاوی محتویات سرآیند فایل iostream در برنامه است. همانطوری که قبلاٌ هم گفته شد، هر برنامهای با اجرای تابع main آغاز میشود(خط ۶). براکت سمت چپ (خط ۷) ابتدا و آغاز بدنه main و براکت متناظر سمت راست (خط ۲۵)، انتها و پایان بدنه را نشان میدهد. خطوط ۹-۱۱ int number1; // first integer to add int number2; // second integer to add int sum; // sum of number1 and number2 بخش اعلان میباشند. کلمات number1، number2 و sum اسامی متغیرها هستند. متغیرها مکانهای در حافظه کامپیوتر هستند که میتوان مقادیری را برای استفاده برنامه در آنها ذخیره کرد. این اعلانها مشخص میکنند که متغیرهایnumber1، number2و sum از نوع داده int هستند، به این معنی که این متغیرها قادر به نگهداری مقادیر صحیح همانند اعداد ۷, -۱۱, ۰, ۳۱۹۱۴ و غیره میباشند. تمامی متغیرها بایستی دارای نام و یک نوع داده باشند تا بتوان از آنها در برنامه استفاده کرد. چندین متغیر از یک نوع را میتوان در خط یا چند خط مجزا از هم اعلان کرد. میتوانیم هر سه متغیر فوق را در یک خط و بصورت زیر اعلان کنیم: int number1, number2, sum; با این همه، در این حالت خوانائی برنامه کاهش یافته و مانع میشود تا توضیحات مناسب برای هر متغیر و هدف از آن متغیر در برنامه را وارد سازیم. اگر بخواهیم بیش از یک متغیر در یک خط اعلان کنیم (همانند مورد بالا) باید اسامی را با یک ویرگول(,) از یکدیگر جدا کنیم. که به این حالت لیست جدا شده با ویرگول گفته میشود. بزودی در مورد نوع داده double (خاص اعداد حقیقی، اعداد با نقطه اعشار همانند۳٫۴ و -۱۱٫۱۹) و نوع داده char (خاص داده کاراکتری، یک متغیر از نوع char فقط میتواند یک حرف کوچک، یک حرف بزرگ، یک رقم یا یک کاراکتر خاص همانند $ یا * را در خود ذخیره سازد) صحبت خواهیم کرد. غالباً به نوعهای همانند double، int وchar نوعهای بنیادین، نوعهای اصلی یا نوعهای توکار (built-in) میگویند. انواع نوعهای بنیادین از جمله کلمات کلیدی هستند و از اینرو باید تماماً با حروف کوچک به نمایش درآیند. نام یک متغیر (همانند number1) میتوانند هر شناسه معتبری که یک کلمه کلیدی نیست، باشد. یک شناسه متشکل از دنبالهای از کاراکترها شامل حروف، ارقام و خط زیرین ( _ ) است. یک شناسه نمیتواند با یک رقم آغاز شود. زبان C++ از جمله زبانهای حساس به موضوع است، به این معنی که مابین حروف کوچک و بزرگ تفاوت قائل میشود. از اینرو شناسههای a1 و A1 با هم برابر نیستند. از اختصار یا کوتهسازی شناسهها اجتناب کنید، تا خوانایی برنامه افزایش یابد. خط ۱۳ std::cout << “Enter first integer: “; // prompt user for data مبادرت به چاپ رشته Enter first integer (بعنوان رشته لیترال نیز شناخته میشود) بر روی صفحه نمایش میکند. به چنین پیغامهای prompt گفته میشود، چراکه به کاربر میگویند که چه کاری باید انجام دهد. خط ۱۴ std::cin >> number1; // read first integer from user into number1 با استفاده از شی ورودی cin (از فضای نامی std ) و عملگر >>، مقداری از صفحهکلید دریافت میکند. با استفاده از عملگر>> به همراه std::cin کاراکترها از یک ورودی استاندارد که معمولا صفحهکلید است، دریافت میشوند. در واقع میتوان گفت که std::cin مقداری برای number1 دریافت میکند. هنگامی که برنامه اجرا شده و به عبارت فوق میرسد، منتظر میماند تا کاربر مقداری برای متغیر number1 وارد سازد. کاربر با تایپ یک مقدار صحیح و سپس فشردن کلید Enter (گاهاٌ به این کلید، کلید Return هم میگویند) به برنامه پاسخ میدهد. با این عمل مقدار تایپ شده به کامپیوتر ارسال میشود. سپس کامپیوتر کاراکترهای دریافت شده را به یک عدد صحیح تبدیل و این عدد (یا مقدار) را به متغیر number1 نسبت میدهد. از این نقطه به بعد هر مراجعهای به number1 در این برنامه مترادف با استفاده از این مقدار خواهد بود. شیهای std::cout و std::cin تعامل مابین کاربر و کامپیوتر را تسهیل میبخشند. بدلیل اینکه این تعامل بفرم یک گفتگو است، غالباٌ به اینحالت محاسبه تعاملی یا محاورهای گفته میشود. خط ۱۶ std::cout << “Enter second integer: “; // prompt user for data مبادرت به چاپ Enter second integer بر روی صفحه نمایش میکند. این عبارت به کاربر اعلان می کند که چه کاری انجام دهد. خط ۱۷ std::cin >> number2; // read second integer from user into number2 مقداری برای متغیر number2 از سوی کاربر بدست میآورد. عبارت تخصیص دهنده در خط ۱۹ sum = number1 + number2; // add the numbers; store result in sum مجموع متغیرهای number1 و number2 را محاسبه و نتیجه آنرا به متغیر sum تخصیص میدهد. از عملگر تخصیص = به این منظور استفاده شده است. مفهوم عبارت فوق به این مضمون است “sum مقدار خود را از number1 + number2 بدست میآورد.” اکثر محاسبات در بین عبارات تخصیصی صورت میگیرند. به عملگر = و عملگر + ، عملگرهای باینری گفته میشود چرا که هر کدامیک از آنها دارای دو عملوند هستند. در مورد عبارت فوق، عملگر + ، دارای دو عملوند number1 و number2 است. همچنین عملگر =، دارای دو عملوند sum و مقدار بدست آمده از number1 + number2 است. خط ۲۱ std::cout << “Sum is ” << sum << std::endl; // display sum; end line مبادرت به چاپ رشته کاراکتری Sum is و بدنبال آن مقدار عددی متغیر sum توسط عبارت std::endl میکند.end1 کوتاه شده “end line” بوده و متعلق به فضاینامی std میباشد. کنترل کننده جریان std::endl یک خط جدید به خروجی منتقل و سپس “بافر خروجی را خالی میکند.” به این معنی که، در برخی از سیستمها خروجی در ماشین انباشته میشود تا زمانیکه “ارزش” نمایش در صفحه نماش را پیدا کنند، عبارت std::endl خروجیهای انباشته شده را مجبور میکند تا در یکباره به نمایش در آیند. دقت کند که عبارت قبلی دو مقدار از انواع مختلف را خارج میسازد. عملگر << از نحوه ارسال هر نوع داده به خروجی مطلع است. به روش استفاده از چندین عملگر << در یک عبارت، عملیات زنجیر کردن، متصل کردن یا فرآیند آبشاری گفته میشود. از اینرو، داشتن چندین عبارت خروجی برای خارج کردن چند داده متمایز ضروری نیست. البته میتوان محاسبات را در بین عبارات خروجی هم انجام داد. میتوانیم عبارات موجود در خطوط ۱۹ و ۲۱ را در یک عبارت، بفرم زیر داشته باشیم std::cout << “Sum is “ << numbeer1 + number2 << std::endl; بنابر این دیگر نیازی به متغیر sum نخواهد بود. براکت راست، {، به کامپیوتر اطلاع میدهد که به انتهای تابع main رسیده است. از جمله توانمندیهای C++ این است که به کاربران اجازه میدهد تا نوع دادههای مورد نیاز و متعلق به خود را ایجاد کنند (در فصل سوم در این مورد و در فصلهای نهم و دهم نگاهی دقیق به این موضوع خواهیم داشت). سپس با نحوه رسیدگی C++ به هنگام کار با نوع دادههای جدید با استفاده از عملگرهای >> و << بیشتر آشنا خواهید شد (مبحث عملگرهای سربارگذاری در فصل یازدهم). ۵-۲ مفاهیم حافظه اسامی متغیرها، همانند number1، number2 و sum مطابق با مکانهای واقعی در حافظه کامپیوتر هستند. هر متغیر دارای یک نام، نوع، سایز و مقدار است. در برنامه جمع ۵-۲ زمانیکه عبارت std::cin >> number1; // read first integer from user into number1 در خط ۱۴ اجرا میشود، کاراکترهای تایپ شده توسط کاربر به یک عدد صحیح تبدیل میشود و در مکانی از حافظه با نام number1 و با کمک کامپایلر C++ قرار داده میشود. فرض کنید که کاربر عدد ۴۵ را به عنوان مقداری برای number1 وارد کند. کامپیوتر ۴۵ را دریافت و در مکان number1 همانند شکل ۶-۲ قرار میدهد. زمانیکه مقداری در یک مکان حافظه قرار میگیرد، مقدار جدید بر روی مقدار قبلی بازنویسی میشود. مجدداٌ به سراغ برنامه جمع میرویم، هنگامی که عبارت std::cin >> number2; // read second integer from user into number2 در خط ۱۷ اجرا شود، فرض کنید کاربر مقدار ۷۲ را وارد سازد، این مقدار وارد مکان number2، شده و تصویر حافظه همانند شکل ۷-۲ خواهد شد. دقت کنید که این مکانهای حافظه ضرورتا، نبایستی در کنار هم قرار داشته باشند. پس از اینکه برنامه مقادیر number1 و number2 را بدست آورد، این مقادیر را با هم جمع کرده و مجموع را در درون متغیر sum قرار میدهد. عبارت sum = number1 + number2; // add the numbers; store result in sum علاوه بر آنکه عمل جمع را انجام میدهد، مقدار حاصله را هم در sum ذخیره میسازد. این عمل زمانی رخ میدهد که مجموع دو متغیر number1 و number2 محاسبه شدهاند و در مکان sum ذخیره میشود. پس از اینکه sum محاسبه شد، حافظه بفرم شکل ۸-۲ تبدیل خواهد شد. توجه نمائید که مقادیر number1 و number2 پس از انجام محاسبه sum همچنان بدون تغییر باقی میماند. با اینکه از این مقادیر استفاده شده است، اما مقادیر اولیه آنها با انجام عمل محاسباتی از بین نمیرود. شکل ۶-۲ | مکان حافظه در حال نمایش نام و مقدار متغیر number1. شکل ۷-۲ | مکانهای حافظه پس از ذخیرهسازی مقادیر برای number1 و number2. شکل ۸-۲| مکانهای حافظه پس از محاسبه sum با استفاده از number1 و number2
پاسخ : آموزش ++C محاسبات اکثر برنامهها محاسبات ریاضی انجام میدهند. عملگرهای ریاضی در جدول شکل ۹-۲ لیست شدهاند. توجه کنید که تمام نمادهای بکار رفته در جبر در C++ بکار گرفته نمیشوند. علامت ستاره (*) نشاندهنده ضرب و علامت درصد (%) نشاندهنده باقیمانده است. اکثر عملگرهای حسابی (در جدول ۹-۲) از نوع عملگرهای باینری هستند چرا که هر عملگر مابین دو عملوند قرار میگیرد. برای مثال، عبارت حسابی number1 + number2 شامل عملگر باینری + و دو عملوند number1 و number2 میباشد. عملیات C++ عملگر محاسباتی عبارت جبری عبارتC++ جمع + f + 7 f + 7 تفریق - p – c p – c ضرب * bm b * m تقسیم / x / y یا یا x / y باقیمانده % r mod s r % s شکل ۹-۲ | عملگرهای محاسباتی. قوانین تقدم عملگر C++ عملگرها را در عبارات محاسباتی با توالی که قانون تقدم عملگرها تعیین میکند بکار میبرد. این قوانین شبیه قوانین موجود در جبر هستند: ۱- عملگرهایی که در درون جفت پرانتز قرار دارند دارای اولویت اول هستند. بنابر این برنامهنویس با استفاده از پرانتز میتواند ترتیب اجرای محاسبات را در دست بگیرد. پرانتزها دارای بالاترین سطح تقدم میباشند. در مواردی که پرانتزها به صورت تودرتو (آشیانهای) قرار گرفتهباشند، عملگر پرانتزی که در داخلیترین سطح قرار دارد ابتدا انجام میگیرد، همانند ((a + b) + C) عملگر قرار گرفته در جفت پرانتز داخلی ابتدا بکار گرفته میشود. ۲- عملگرهای ضرب و تقسیم و باقیمانده در مرحله بعدی بکار گرفته میشوند. اگر عبارتی شامل چندین عملگر ضرب، تقسیم و باقیمانده باشد، عملگرها از سمت چپ به راست اجرا خواهند شد. ضرب، تقسیم و باقیمانده دارای اولویت هم سطح یا برابر هستند. ۳- عملگرهای جمع و تفریق در آخرین مرحله به کار میروند. اگر عبارتی شامل چندین عملگر تفریق و جمع باشد، عملگرها به ترتیب از سمت چپ به راست اجرا خواهند شد. عملگرهای جمع و تفریق دارای اولویت هم سطح هستند. وجود قوانین تقدم عملگرها، زبان C++ قادر میسازد تا عملگرها را با ترتیب صحیح بکار گیرد. در جدول شکل ۱۰-۲ خلاصهای از قوانین تقدم عملگرها آورده شده است. این جدول با معرفی عملگرهای دیگر C++ در فصلهای بعدی تکمیلتر خواهد شد. جدول کامل تقدم عملگرها در پیوست کتاب موجود است. عملگر(ها) عملیات ترتیب ارزیابی (تقدم) ( ) پرانتز اولویت اول. اگر پرانتزها به صورت تودرتو باشند، عبارتی که در درون داخلیترین پرانتز قرار دارد ابتدا محاسبه میشود. اگر چندین جفت پرانتز در یک خط قرار گرفته باشند (تودرتو نباشند) ترتیب اجرا از سمت چپ به راست خواهد بود. *, / , % ضرب و تقسیم و باقیمانده اولویت دوم. اگر چند مورد از چنین عملگرهای وجود داشته باشد، ترتیب اجرا از سمت چپ به راست خواهد بود. +, - جمع و تفریق اولویت آخر. اگر چندین عملگر جمع و تفریق وجود داشته باشد ترتیب اجرا از سمت چپ به سمت راست خواهد بود. شکل ۱۰-۲ | تقدم عملگرهای محاسباتی. عبارات ساده جبری و C++ حال اجازه دهید تا به چند عبارت محاسباتی نگاهی بیاندازیم تا بخوبی با قوانین تقدم عملگرهای محاسباتی آشنا شوید. در هر مثالی که ذکر میشود عبارت جبری و معادل C++ آن عبارت نیز آورده شده است. مثال زیر یک عبارت ریاضی را نشان میدهد که منظور از آن به دست آوردن میانگین پنج عدد است: :جبری m = (a + b + c + d + e) / 5;: C++ وجود پرانتز در این عبارت ضروری است چرا که عملگر تقسیم تقدم بالاتری نسبت به عملگر جمع دارد، در نتیجه مقدار داخلی پرانتز بر ۵ تقسیم میشود. اگر پرانتز در این عبارت حذف شود، منظور محاسبه a + b + c + d + e / 5 خواهد بود که معادل عبارت زیر است: عبارت زیر نشاندهنده یک معادله است: y = mx + b :جبری y = m * x + b; : C++ وجود پرانتز در این عبارت نیاز نیست، چرا که عملگر ضرب تقدم بالاتری نسبت به عملگر جمع دارد و در ابتدا انجام میشود. عمل تخصیص در آخرین مرحله صورت میگیرد چرا که به نسبت عمل ضرب و جمع از اولویت پایینتری برخوردار است. مثالی که در زیر آورده شده حاوی عملگرهای توان، ضرب، تقسیم اعشاری، جمع و تفریق است: z = pr%q + w / x – y : جبری : C++ دایرههای حاوی اعداد نشاندهنده ترتیب اجرای عملگرها هستند. عملگر ضرب در اولویت اول قرار دارد و عملگرهای باقیمانده و تقسیم در اولویتهای بعدی و به ترتیب از سمت چپ به راست اجرا میشوند و پس از آنها عملگرهای جمع و تفریق به ترتیب اجرا شده و در پایان عمل تخصیص صورت میگیرد. توجه کنید که در این جدول در مورد پرانتزهای تودرتو مطالبی بیان شده است، اما تمام عبارات محاسباتی که دارای چندین جفت پرانتز هستند، ممکن است حاوی پرانتزهای تودرتو نباشد. برای مثال، اگر چه عبارت زیر a * (b + c) + c * (d + e) شامل دو جفت پرانتز است، اما هیچ کدامیک از آنها پرانتز تودرتو نمیباشند. در چنین حالتی هر دو آنها دارای سطح یکسان میباشند. ارزیابی معادله درجه دوم برای درک بهتر قوانین تقدم عملگرها به مثال زیر که یک چند جملهای درجه دوم است توجه کنید: دایرههای حاوی اعداد نشاندهنده ترتیب اجرای عملگرها هستند. در C++ عملگر محاسباتی برای انجام عمل توان وجود ندارد، از اینرو عبارت x[sup]2[/sup] را بصورت x * x نشان دادهایم. بزودی در مورد تابع استاندارد کتابخانهای pow (توان) صحبت خواهیم کرد. ۷-۲ تصمیمگیری: عملگرهای مقایسهای و رابطهای در این قسمت به معرفی ساختارif در C++ میپردازیم که بر مبنای برقرار بودن یا نبودن برخی از شرطها اقدام به تصمیمگیری میکند. عبارت موجود در یک ساختارif ، شرط نامیده میشود. اگر شرط مورد قبول واقع شود (شرط true باشد) عباراتی که در داخل بدنه ساختار if قرار گرفتهاند اجرا میشوند و اگر شرط مورد قبول واقع نشود عبارات داخل بدنه اجرا نخواهند شد. برای آشنائی شما با چنین ساختاری به بررسی یک مثال خواهیم پرداخت. شرطهای که در ساختار if بکار میروند میتوانند از عملگرهای مقایسهای و رابطهای که در جدول شکل ۱۲-۲ آورده شدهاند، استفاده کنند. تمام عملگرهای رابطهای دارای اولویت یکسان بوده و از سمت چپ به راست ارزیابی میشوند. عملگرهای مقایسهای به نسبت عملگرهای رابطهای از اولویت پایینتری برخوردار هستند و آنها هم از سمت چپ به راست ارزیابی میشوند. Y = 2 * 5 * 5 + 3 * 5 + 7; گام اول سمت چپترین ضرب ۲ * ۵= ۱۰ Y = ۱۰ * ۵ + ۳ * ۵ + ۷; گام دوم سمت چپترین ضرب ۱۰ * ۵ = ۵۰ Y = 50 + 3 * 5 + 7; گام سوم ضرب قبل از جمع ۳ * ۵ = ۱۵ Y = 50 + 15 + 7; گام چهارم سمت چپترین جمع ۵۰ + ۱۵ = ۶۵ Y = 65 + 7; گام پنجم آخرین جمع ۶۵ + ۷ = ۷۲ Y = 72; گام ششم شکل ۱۱-۲ | ترتیب اجرای عملگرهای محاسباتی در یک چند جملهای درجه دوم. عملگرهای مقایسهای یا رابطهای استاندارد در جبر عملگرهای مقایسهای یا رابطهای درC++ مثال در C++ مفهوم شرط عملگرهای مقایسهای = == x ==y x با y برابر است. != x!=y x با y برابر نیست. عملگرهای رابطهای > > x>y x از y بزرگتر است. < < x<y x از y کوچکتر است. >= x>=y x بزرگتر یا مساوی y است. <= x<=y x کوچکتر یا مساوی y است. شکل ۱۲-۲ | عملگرهای مقایسهای و رابطهای. برنامه زیر از شش عبارت if برای مقایسه بین دو عدد ورودی از سوی کاربر استفاده میکند. اگر شرط موجود در هر کدامیک از عبارات if برقرار باشد (true)، خروجی مرتبط با آن عبارت به اجرا درخواهد آمد. برنامه شکل ۱۳-۲ نشاندهنده برنامه و کادرهای ورودی و خروجی از اجرای نمونه برنامه است. شکل ۱۳-۲ | عملگرهای رابطهای و مقایسهای. در خطوط ۶-۸ using std::cout; // program uses cout using std::cin; // program uses cin using std::endl; // program uses endl از عبارات using استفاده شده است که نیاز به تکرار پیشوند std:: را برطرف میکند. پس از بکارگیری عبارات using، میتوانیم cout را بجای std::cout، cin را بجای std::cin و endl را بجای std::endl در مابقی برنامه بنویسیم.]توجه: از این نقطه به بعد در کتاب، در هر مثال از یک یا چند عبارت using استفاده شده است.[ در خطوط ۱۳-۱۴ int number1; // first integer to compare int number2; // second integer to compare متغیرهای مورد نیاز برنامه اعلان شدهاند. بخاطر دارید که متغیرها میتوانند در یک خط یا چند خط اعلان شوند. برنامه در خط ۱۷ از روش آبشاری برای دریافت داده به منظور دریافت دو عدد صحیح استفاده کرده است. بخاطر دارید که امکان نوشتن cin را به توجه به خط ۷ فراهم آوردهایم (بجای std::cin). اولین مقدار قرائت شده و در متغیر number1 قرار داده میشود و سپس مقدار دوم قرائت شده و در متغیر number2 ذخیره میگردد. ساختار if در خطوط ۱۹-۲۰ if ( number1 == number2 ) cout << number1 << ” == ” << number2 << endl; مبادرت به مقایسه متغیرهای number1 و number2 برای تست برابر بودن میکند. اگر مقادیر برابر باشند، عبارت موجود در خط ۲۰ جمله مبنی بر اینکه اعداد با هم برابر هستند به نمایش در میآورد. اگر شرطی در یک یا چند ساختار if که در خطوط ۲۳, ۲۶, ۲۹,۳۲ و ۳۶ قرار دارند برقرار شود، عبارت متناظر توسط cout در خروجی به نمایش در میآید. دقت کنید که هر ساختار if در برنامه شکل ۱۳-۲ دارای یک عبارت در بدنه خود بوده و بدنه تمام ساختارها بفرم دندانهدار نوشته شدهاند. با دندانهدار نوشتن ساختار هر if وضوح و خوانائی برنامه را افزایش دادهایم. در فصل چهارم نشان خواهیم داد که چگونه میتوان در ساختارهای if از چند عبارت استفاده کرد ( به کمک جفت کاراکتر { } ). به نحوه استفاده از فاصلهها در برنامه شکل ۱۳-۲ دقت کنید. در عبارات C++، کاراکترهای white-space همانند تبها، خطوط جدید توسط کامپایلر در نظر گرفته نمیشوند. (اگر در درون رشته بکار گرفته شوند در نظر گرفته خواهند شد.) از اینرو، امکان دارد عبارات بر روی چند خط تقسیم شده و برطبق نظر برنامهنویس از هم فاصله پیدا کنند. جدا کردن هویتها یا مشخصهها، رشتهها (همانند “hello” ) و ثابتها (همانند عدد ۱۰۰۰ ) بر روی چند خط خطای نحوی خواهد بود. در جدول شکل ۱۴-۲ تقدم عملگرهای معرفی شده در این فصل بطور یکجا آورده شدهاند. اولویت عملگرها از بالا به پایین کاهش مییابد. دقت کنید که تمام این عملگرها بجر از عملگر تخصیص =، از سمت چپ به راست ارزیابی میشوند. عملگر شرکتپذیری نوع ( ) چپ به راست پرانتز * / % چپ به راست ضرب، تقسیم،باقیمانده + - چپ به راست جمع، تفریق << >> چپ به راست ورود و خروج داده < <= > >= چپ به راست رابطهای == != چپ به راست مقایسهای = راست به چپ تخصیصی شکل ۱۴-۲ تقدم عملگرهای معرفی شده تا بدین مرحله.
پاسخ : آموزش ++C مبحث آموزشی مهندسی نرمافزار: بررسی نیازمندیهای ATM در این بخش طراحی و پیادهسازی شیگرا، مبحث آموزشی مهندسی نرمافزار را آغاز میکنیم. بخشهای «مبحث آموزشی مهندسی نرمافزار» که در انتهای این فصل و چند فصل بعدی قرار داده شدهاند، شما را به آسانی وارد بحث شیگرایی خواهند کرد. نرمافزاری برای یک سیستم ماشین تحویلدار خودکار ATM ایجاد خواهیم کرد، که تجربه مناسبی در زمینه طراحی و پیادهسازی برایتان به ارمغان خواهد آورد. در فصلهای ۷-۳ و ۱۳، مراحل مختلفی از فرآیند طراحی شیگرا (OOD) را با استفاده از UML انجام خواهیم داد، و در کنار آن، مباحث مرتبط نیز در خود فصلها مطرح میشوند. ضمیمهای در ارتباط با پیادهسازی ATM با استفاده از تکنیکهای برنامهنویسی شیگرا (OOP) در C++ آورده شده است. بحث ما یک بحث کاملاً آموزشی است، و حالت تمرینی ندارد و شما را کاملاً درگیر جزئیات کار با کد C++ میکند که پیادهسازیکننده برنامه هستند. این مطالب شما را با انواع مسائل قابل توجه در صنایع و همچنین راهحلهای موجود آشنا خواهند کرد. فرآیند طراحی را با معرفی مستند نیازمندیها شروع میکنیم که تصریحکننده کل آنچیزی است که از یک سیستم ATM انتظار انجام آن را داریم و بطور دقیق آن را بررسی خواهیم کرد. مستند نیازها فرض کنید یک بانک محلی مایل است تا یک سیستم ATM جدید را بکار گیرد و به کاربران (مشتریان بانک) اجازه دهد تا تعاملات مالی خود را با آن انجام دهند (شکل ۱۵-۲). هر کاربر میتواند فقط یک حساب در بانک داشته باشد. کاربران ATM باید قادر به دیدن موجودی حساب، برداشت از حساب و پسانداز باشند. واسط کاربر ATM حاوی کامپونتهای سختافزاری زیر است: * یک صفحه نمایش که پیغامها را به کاربر بنمایش درمیآورد * یک صفحه کلید که ورودی عددی را از کاربر دریافت مینماید * تحویلدار خودکار که پول را به کاربر تحویل میدهد * شکاف سپرده که پاکت سپرده را از کاربر تحویل میگیرد. تحویلدار خودکار هر روز با پانصد عدد ۲۰ دلاری پر میشود. [نکته: به این علت که این مبحث آموزشی است، برخی از عناصر مشخص ATM توصیف شده در اینجا، دقیقاً مطابق با ATM واقعی نیستند. برای مثال، معمولاً در یک ATM واقعی دستگاهی وجود دارد که شماره حساب مشتری را از یک کارت ATM میخواند، در حالیکه در ATM ما از کاربر خواسته میشود که شماره حساب خود را از طریق صفحه کلید وارد کند. همچنین در یک ATM واقعی قبض رسید در پایان هر عملیات یا جلسه چاپ میشود. امام تمام خروجیها در این ATM بر روی صفحه نمایش ظاهر میشوند.] بانک از شما میخواهد تا برنامهای جدید برای انجام تعاملات مالی مشتریان بانک از طریق ATM توسعه دهید. بانک بعداً نرمافزار را با سختافزار ATM مجتمع خواهد کرد. نرمافزار بایستی عملکرد دستگاههای سختافزاری (همانند پرداختکننده پول، دریافتکننده سپرده) را در درون کامپونتهای نرمافزاری کپسوله کند، اما نیازی ندارد که از عملکرد داخلی و جزئیات آنها مطلع باشد. فعلاً بخش سختافزاری ATM تولید نشده است، از اینرو بجای نوشتن نرمافزاری که بروی ATM اجرا شود، باید نسخه اولیه از نرمافزار را برای اجرا بر روی یک کامپیوتر شخصی ایجاد کنید. این نسخه از برنامه، از مانیتور کامپیوتر برای شبیهسازی صفحه نمایش ATM و صفحه کلید کامپیوتر برای شبیهسازی، صفحه کلید ATM استفاده خواهد کرد. یک جلسه ATM متشکل از تصدیق یا تایید کاربر (اثبات هویت کاربر) برپایه شماره حساب و شماره شناسایی شخصی (PIN) بوده و در ادامه تعاملات مالی صورت میگیرد. برای تایید کاربر و انجام تعاملات، بایستی ATM با پایگاه داده اطلاعات حساب بانکی در تعامل قرار گیرد. [نکته: پایگاه داده یک مجموعه سازماندهی شده از اطلاعات ذخیره شده در یک کامپیوتر است.] برای هر حساب بانکی، پایگاه داده یک شماره حساب، یک PIN و یک موجودی که نشاندهنده مقدار پول در آن حساب است، در خود ذخیره میسازد. [نکته: برای سادهتر شدن کار، فرض میکنیم که هدف بانک فقط داشتن یک دستگاه ATM است، بنابراین نیازی نیست که نگران نحوه دسترسی همزمان چندین ATM به این پایگاه داده باشید. علاوه بر این، فرض میکنیم که بانک هیچ تغییری در زمان استفاده کاربر (مشتری) از ATM، در پایگاه داده اعمال نمیکند. همچنین هر سیستم تجاری همانند ATM به دلایل قابل قبولی در ارتباط با مباحث امنیتی است که خارج از قلمرو تحصیلی یک دانشجوی ترم اول یا دوم کامپیوتر است.] در اولین برخورد، مشتری با ATM باید توالی از رویدادهای زیر رخ دهند (به شکل ۱۵-۲ توجه نمائید): ۱- صفحة نمایش پیغام خوشآمدگویی (Welcome) را بنمایش درآورده و از کاربر میخواهد تا شماره حساب خود را وارد سازد. ۲- کاربر شماره حساب پنجرقمی خود را با استفاده از صفحه کلید وارد میسازد. ۳- در صفحه نمایش از کاربر خواسته میشود تا PIN را وارد سازد، که مرتبط با شماره حساب است. ۴- کاربر از طریق صفحه کلید، PIN پنج رقمی خود را وارد میسازد. ۵- اگر شماره حساب و PIN ورودی معتبر باشند، ظاهر صفحه نمایش همانند شکل ۱۶-۲ بوده و منوی اصلی در آن ظاهر میگردد. اگر شماره حساب یا PIN اشتباه باشد، پیغام مناسب در صفحه نمایش ظاهر شده و ATM به مرحله اول باز میگردد تا فرآیند تایید را از نو آغاز کند. پس از تایید کاربرد از سوی ATM، منوی اصلی (شکل ۱۶-۲) لیستی از گزینههای عددی برای هر سه نوع تراکنش بنمایش در میآورد: درخواست موجودی (گزینه ۱)، برداشت (گزینه ۲) و سپردهگذاری (گزینه ۳). همچنین منوی اصلی، گزینهای برای خروج از سیستم در اختیار کاربر قرار میدهد (گزینه ۴). پس از نمایش منوی اصلی، کاربر میتواند تراکنش موردرنظر خود را از طریق وارد کردن ۱، ۲، ۳ یا خروج از سیستم، ۴ انتخاب کند. اگر کاربر گزینه اشتباهی را وارد سازد، پیغامی به نمایش درخواهد آمد، و سپس مجدداً منوی اصلی بنمایش در میآید. اگر کاربر گزینة ۱ را انتخاب کند (با وارد کردن عدد ۱) تا از میزان موجودی خود مطلع گردد، این امر صورت خواهد گرفت. برای انجام این کار، بایستی ATM میزان موجودی را از پایگاه داده بانک بازیابی کند. مراحل زیر زمانی رخ میدهند که کاربر گزینه ۲ را برای برداشت پول انتخاب کرده باشد: ۶- منوی در صفحه نمایش ظاهر میشود (شکل ۱۷-۲) که حاوی مقادیر استاندارد قابل پرداخت است. (گزینه ۱) $۲۰، (گزینه ۲) $۴۰، (گزینه ۳) $۶۰، (گزینه ۴)، $۱۰۰ و (گزینه ۵) $۲۰۰٫ همچنین این منو دارای یک گزینه برای لغو تراکنش کاربر است (گزینه ۶). ۷- کاربر با استفاده از صفحه کلید، انتخاب خود را انجام میدهد (گزینههای۶-۱). ۸- اگر مقدار درخواستی برای برداشت، بیشتر از میزان موجودی کاربر باشد، پیغامی این مطلب را به عرض کاربر رسانده و از وی میخواهد که مقدار کمتری تقاضا کند. سپس ATM به مرحله اول باز میگردد. اگر مقدار درخواستی کمتر از موجودی یا برابر آن باشد (یک مقدار قابل قبول)، ATM مرحله ۴ را آغاز خواهد کرد. اگر کاربر مبادرت به لغو تراکنش کند (گزینه ۶)، ATM منوی اصلی را به نمایش در آورده و منتظر ورودی کاربر میشود (شکل ۱۶-۲). ۹- اگر پرداختکننده خودکار پول، به میزان کافی پول برای برآورده کردن تقاضای مشتری داشته باشد، ATM وارد مرحله ۵ خواهد شد. در غیر اینصورت، صفحه نمایش پیغامی مبنی بر اینکه میزان پول دستگاه کمتر از مقدار درخواستی است از کاربر میخواهد که مقدار کمتری را انتخاب نماید. سپس ATM به مرحله اول بازمیگردد. ۱۰- ATM مقدار پول برداشتی را از موجودی حساب کاربر در پایگاه داده بانک کم میکند. ۱۱- پرداختکننده خودکار، مقدار پول درخواستی را به کاربر تحویل میدهد. ۱۲- پیغامی در صفحه نمایش ظاهر شده و به کاربر یادآوری میکند، پول را بردارد. مراحل زیر زمانی رخ میدهند که کاربر عدد ۳ را از منوی اصلی به منظور سپردهگذاری انتخاب کرده باشد: ۱۳- در صفحه نمایش، به کاربر اعلان میشود که مقدار سپردهگذاری خود را وارد سازد یا برای لغو تراکنش صفر را وارد کند. ۱۴- کاربر از طریق صفحه کلید، مقدار سپردهگذاری یا صفر را وارد میسازد [نکته: صفحه کلید دارای نقطه اعشار یا نماد دلار نمیباشد، از اینرو، کاربر نمیتواند یک مقدار دلاری واقعی همانند $۱٫۲۵ را وارد سازد. بجای آن، کاربر باید مقدار سپرده خود را بعنوان یک عدد از سنتها وارد کند (مثلاً (۱۲۵٫ سپس ATM این عدد را بر ۱۰۰ تقسیم میکند، تا عددی که نشاندهنده مقدار دلاری است بدست آید (مثلاً (۱۲۵÷۱۰۰=۱٫۲۵]. ۱۵- اگر کاربر میزان سپرده را مشخص سازد، ATM به مرحله ۴ میرود. اگر کاربر، مبادرت به لغو تراکنش کند (با وارد کردن صفر)، ATM منوی اصلی را به نمایش درآورده و در انتظار ورودی کاربر باقی میماند. ۱۶- در صفحة نمایش، پیغامی به کاربر اعلان میکند که پاکت سپرده را در شکاف سپرده قرار دهد. ۱۷- اگر شکاف سپرده، پاکت سپرده را در عرض دو دقیقه دریافت نماید، ATM مبادرت به افزایش اعتبار کاربر، در میزان موجودی وی در پایگاه داده بانک میکند [نکته: این مقدار پول، بلافاصله برداشت نمیشود. ابتدا باید بانک به لحاظ فیزیکی مبادرت به بازبینی پول نقد و چکهای موجود در پاکت کرده و پس از تایید بانک، حساب کاربر در پایگاه داده به روز میشود. این عملیات مستقل از سیستم ATM صورت میگیرد.] اگر در مدت زمان مشخص شده، شکاف سپرده، پاکتی دریافت نکند، پیغامی در صفحه نمایش مبنی بر لغو تراکنش از طرف سیستم ظاهر خواهد شد. سپس ATM منوی اصلی را به نمایش درآورده و منتظر ورودی کاربر باقی ماند. پس از اجرای موفقیتآمیز یک تراکنش توسط سیستم، باید مجدداً منوی اصلی بنمایش درآید (شکل ۱۶-۲) تا کاربر قادر به انجام تراکنشهای دیگر باشد. اگر کاربر گزینه ۴ را انتخاب کند (خروج از سیستم)، پیغام تشکر بنمایش درآمده و سپس پیغام خوشآمدگویی، برای کاربر بعدی بنمایش درمیآید. تحلیل سیستم ATM بخش قبلی مثال ساده شدهای از یک مستند نیازمندیها بود. بطور نمونه، چنین مستندی، نتیجهای از نیازمندیهای جمعآوری شده است که شامل مصاحبه با کاربران اصلی سیستم و متخصصین در فیلدهای مربوط به سیستم است. برای مثال، تحلیلگر سیستم کسی است که برای آماده کردن مستند نیازمندیهای نرمافزار بانک استخدام میشود (مثلاً سیستم ATM که در اینجا توضیح داده شده است) و میتواند با متخصصین امور مالی مصاحبه انجام دهد تا درک دقیقی از کاری که نرمافزار باید انجام دهد بدست آورد. تحلیلگر از اطلاعات بدست آمده، لیستی از نیازهای سیستم جمعآوری میکند تا طراحان سیستم را بخوبی راهنمایی کند. فرآیند جمعآوری اطلاعات نیازمندیها، یک وظیفه کلیدی در مرحله اول چرخه زندگی نرمافزار است. چرخه زندگی نرمافزار، تصریحکننده مراحلی است که نرمافزار از بدو تولد تا زمان بازنشستگی طی میکند. بطور نمونه این مراحل عبارتند از: تحلیل، طراحی، پیادهسازی، تست و خطایابی، استفاده، نگهداری و بازنشستگی. چندین مدل برای چرخه طول عمر نرمافزار وجود دارد که هر یک دارای مزایا و مشخصات خاص بوده که روش انجام مراحل مختلف را به مهندسان نرمافزار گوشزد میکنند. مدل آبشاری (waterfall model) هر مرحله را یکی بعد از دیگری انجام میدهد، در حالیکه مدل تکرارکننده (iterative model) میتواند مراحل را یک یا چندین بار در فرآیند چرخه طول عمر یک محصول تکرار نماید. مرحله تحلیل در چرخه طول عمر نرمافزار، متمرکز بر تعریف مسئله برای حل کردن آن است. به هنگام طراحی هر سیستمی، باید مسئله بدرستی حل شود (راه حل باید صحیح باشد). تحلیلگران سیستم مبادرت به جمعآوری نیازهایی میکنند که قادر به حل مسئله مشخصی هستند. مستند نیازها که آن را در اینجا برای سیستم ATM مطرح کردهایم، بقدر کافی گویا است و نیازی نیست که وارد یک مرحله تحلیل اضافیتر شوید. برای ثبت اینکه سیستم مورد نظر چه کاری باید انجام دهد، غالباٌ توسعهدهندگان از تکنیکی بنام مدلسازی use case (حالت استفاده) کمک میگیرند. این فرآیند حالات مورد استفاده از سیستم را معین میسازد، که هر یک نشانگر یک قابلیت مختلف است که سیستم در اختیار سرویسگیرندگان خود قرار میدهد. برای مثال، بطور نمونه ATMها دارای چندین حالت استفاده همانند «نمایش میزان موجودی»، «برداشت پول»، «سپردهگذاری»، «انتقال پول مابین حسابها» و «خرید تمبر پستی» هستند. سیستم ATM ساده شده که قصد ساخت آن را داریم، فقط دارای سه حالت استفاده است که در شکل ۱۸-۲ دیده میشود. هر حالت استفاده توصیفکننده یک سناریو است که کاربر از سیستم استفاده میکند. در حال حاضر حالات استفاده از سیستم ATM را از مستند نیازها مطالعه کردهاید، که هر مرحله نیازمند نوعی از تراکنش است (نمایش موجودی، برداشت پول و سپردهگذاری)، که آنها را در سه حالت استفاده از ATM قرار دادهایم. دیاگرامهای Use Case در این بخش مبادرت به معرفی یکی از چندین دیاگرام UML برای ATM مطرح شده در این مبحث آموزشی میکنیم. یک دیاگرام حالت استفاده (use case) برای مدل کردن تراکنشهای مابین سرویسگیرندگان سیستم (در این مورد، مشتریان بانک مدنظر هستند) و سیستم ایجاد میکنیم. هدف، نمایش انواع تراکنشهای کاربران با سیستم است بدون اینکه جزئیات را وارد کار نمائیم (جزئیات در دیاگرامهای دیگر UML تدارک دیده میشوند، که در ادامه با آنها مواجه خواهید شد). غالباً دیاگرامهای حالت استفاده همراه با جملات غیررسمی که توصیفکننده دقیقتر حالات استفاده هستند، همراه میباشند، همانند جملاتی که در مستند نیازها دیده میشود. دیاگرامهای حالت استفاده در مرحله تحلیل چرخه عمر نرمافزار تولید میشوند. در سیستمهای بزرگتر، دیاگرامهای حالت استفاده ساده هستند اما جزء ابزارهای ضروری هستند که به طراحان سیستم کمک میکنند تا تمرکز خود را بر روی نیازهای کاربران حفظ کنند. شکل ۱۸-۲ نمایشی از دیاگرام حالت استفاده برای سیستم ATM است. تصویر آدمک نشاندهنده یک بازیگر (actor) است که تعریفکننده نقشهای است که یک موجودیت خارجی همانند یک شخص یا سیستم دیگر، به هنگام در تعامل قرار گرفتن با سیستم ایفاء میکند. برای سیستم ATM ما، بازیگر کاربری است که میتواند میزان موجودی را مشاهده کند، از سیستم پول دریافت و در آن سپردهگذاری انجام دهد. کاربر یک شخص حقیقی نیست، اما دارای نقشهای از یک شخص واقعی است (زمانیکه نقشی از یک کاربر را باز میکند) و میتواند آن نقشها را در زمان تعامل با ATM بازیکند. دقت کنید که دیاگرام حالت استفاده میتواند حاوی چندین بازیگر باشد. برای مثال، دیاگرام حالت استفاده در یک سیستم ATM واقعی، میتواند شامل یک بازیگر بنام Administrator (مدیر) باشد که مسئول پر کردن هر روز پول در تحویلدار است. شناسایی بازیگر در سیستم را با بررسی مستند نیازها مشخص میکنیم که عبارت است از “کاربران ATM بایستی قادر به مشاهده میزان موجودی، برداشت پول و سپردهگذاری باشند.” از اینرو، بازیگر در هر یک از این سه حالت استفاده، کاربری است که با ATM در تعامل قرار میگیرد. موجودیت خارجی، یک شخص حقیقی، بخشی از نقش کاربر را در انجام تراکنشهای مالی بازی میکند. در شکل ۱۸-۲ یک بازیگر بنام «کاربر» نشان داده شده است. UML هر کدامیک از حالات استفاده را بصورت یک بیضی متصل به بازیگر توسط یک خط ساده را مدل میکند. بایستی مهندسان نرمافزار یا بطور دقیقتر طراحان سیستم، مبادرت به تحلیل مستند نیازها یا تنظیم حالات استفاده و طراحی سیستم قبل از شروع به برنامهنویسی با یک زبان برنامهنویسی خاص کنند. در مدت زمان مرحله تحلیل، طراحان سیستم بر درک مستند نیازها تمرکز دارند تا مشخصاتی با معیار بالا بدست آید که توصیفکننده آنچه که سیستم باید انجام دهد، باشد. خروجی مرحله طراحی (طراحی مشخصهها) بایستی بقدر کافی واضح و شفاف باشد که نحوه ایجاد سیستم را برای برآورده کردن نیازها بیان کند. در چند بخش بعدی «مبحث آموزشی مهندسی نرمافزار» این مراحل را از طریق طراحی شیگرا (OOD) بر روی سیستم ATM به منظور تولید طرحی از مشخصهها انجام خواهیم داد که حاوی مجموعهای از دیاگرامهای UML و جملات پشتیبانیکننده است. بخاطر دارید که UML برای استفاده در هر فرآیند OOD طراحی شده است. چندین پردازشکننده وجود دارد که یکی از بهترین آنها RUP (Rational Unified Process) است که توسط شرکت Rational Software توسعه پیدا کرده است (این شرکت تبدیل به بخشی از IBM شده است). RUP یک پردازشکننده بسیار مناسب و مطلوب برای طراحی برنامههای کاربردی در سطح صنایع است. برای این مبحث آموزشی، فرآیند طراحی ساده شده خود را معرفی میکنیم. طراحی سیستم ATM در این بخش وارد مرحله طراحی سیستم ATM میشویم. سیستم مجموعهای از کامپونتها است که برای حل مسئلهای با هم در تعامل قرار میگیرند. برای مثال، برای اینکه سیستم ATM وظایف تعیین شده را انجام دهد، باید دارای یک واسط کاربر بوده (شکل ۱۵-۲) و حاوی نرمافزاری باشد که قادر به انجام تراکنشهای مالی و کار با پایگاه داده اطلاعات حساب مشتریان در بانک باشد. ساختار سیستم، توصیفکننده شیهای سیستم و روابط داخلی آنها است. رفتار سیستم توصیفکننده نحوه تغییر عملکرد شیهای سیستم و برقراری روابط بین آنها است. هر سیستمی دارای ساختار و رفتار است، که طراحان باید آنها را مشخص سازند. چندین نوع مشخص از ساختار و رفتار سیستم وجود دارد. برای مثال، تعامل مابین شیها در یک سیستم، متفاوت از تعامل مابین سیستم و کاربر است، با این همه هنوز هر دو بخشی از رفتار سیستم محسوب میشوند. نسخه UML 2 تصریحکننده ۱۳ نوع دیاگرام برای مستند کردن مدلهای سیستم است. هر دیاگرام صفات مشخصی از ساختار سیستم یا رفتار آن را مدلسازی میکند، شش دیاگرام در ارتباط با ساختار سیستم بوده و هفت دیاگرام باقیمانده مربوط به رفتار سیستم هستند. در اینجا فقط شش نوع دیاگرام بکار رفته در این مبحث را لیست کردهایم، یکی از آنها بنام دیاگرام کلاس، مبادرت به مدل کردن ساختار سیستم میکند و مابقی در ارتباط با مدلسازی رفتار سیستم هستند. ۱- دیاگرام حالت استفاده، همانند شکل ۱۸-۲، مبادرت به مدلسازی تعامل صورت گرفته مابین سیستم و موجودیت خارجی آن (بازیگران) با جملات حالت استفاده میکند (قابلیتهای سیستم همانند «نمایش میزان موجودی»، «برداشت پول» و «سپردهگذاری»). ۲- دیاگرامهای کلاس، که در بخش ۱۱-۳ با آنها آشنا خواهید شد. این دیاگرامها در مدل کردن کلاسها یا «ایجاد بلوکهای» مورد استفاده در سیستم کاربرد دارند. هر اسم یا «چیز» توصیف شده در مستند نیازها، نامزد تبدیل شدن به یک کلاس در سیستم است (همانند «حساب»، «صفحه کلید»). دیاگرامهای کلاس در مشخص کردن روابط ساختاری موجود مابین اجزای سیستم نقش دارند. برای مثال، دیاگرام کلاس سیستم ATM مشخص میکند که ATM به لحاظ فیزیکی متشکل از یک صفحه نمایش، صفحه کلید، تحویلدار خودکار پول و شکاف سپردهگذاری است. ۳- دیاگرامهای وضعیت ماشین، که در بخش ۱۱-۳ با آنها آشنا خواهید شد. این دیاگرامها در مدلسازی روشهای که یک شی تغییر وضعیت یا حالت میدهد کاربرد دارند. وضعیت یک شی توسط مقادیری که از صفات شی در زمان اجرا بدست میآیند، تعیین میشود. زمانیکه وضعیت یک شی تغییر پیدا میکند، امکان دارد شی رفتار متفاوتی در سیستم بخود بگیرد. برای مثال، پس از اعتبارسنجی PIN کاربر، تراکنش ATM از وضعیت «کاربر تایید نشده» به وضعیت «کاربر تایید شده» تبدیل شده و در این لحظه ATM به کاربر اجازه میدهد تا تراکنشهای مالی انجام دهد (مشاهده میزان موجودی، برداشت پول و سپردهگذاری). ۴- دیاگرامهای فعالیت، که در بخش ۱۱-۵ با آنها آشنا خواهید شد. این دیاگرامها در مدلسازی فعالیت یک شی کاربرد دارند (جریان کار یا روند کار یک شی (توالی از رویدادها) در مدت زمان اجرای برنامه). یک دیاگرام فعالیت مبادرت به مدل کردن اعمال یک شی و همچنین ترتیب انجام این اعمال را مشخص مینماید. برای مثال، یک دیاگرام فعالیت نشان میدهد که ATM باید میزان موجودی حساب کاربر را (از پایگاه داده اطلاعات حساب) قبل از اینکه صفحه نمایش موجودی را بنمایش درآورد، تهیه نماید. ۵- دیاگرامهای ارتباطی (در نسخههای قبلی UML این دیاگرام، دیاگرامهای همکاری نامیده میشود) مدلکننده تعاملهای صورت گرفته مابین شیهای یک سیستم هستند، با تاکید بر اینکه کدام تعامل رخ دهد. در بخش ۱۲-۷ با این نوع دیاگرامها آشنا خواهید. برای مثال باید ATM با پایگاه داده حساب بانکی ارتباط برقرار کند تا میزان موجودی را بازیابی نماید. ۶- دیاگرامهای توالی، این دیاگرامها نیز مبادرت به مدلسازی تعاملهای صورت گرفته مابین شیهای یک سیستم میکنند، اما برخلاف دیاگرامهای ارتباطی، تاکید آنها بر زمان رخ دادن تعاملها است. در بخش ۱۲-۷ با این نوع دیاگرامها آشنا خواهید شد. برای مثال، صفحه نمایش به کاربر اطلاع میدهد که مقدار پولی که میخواهد برداشت کند را قبل از پرداخت وارد نماید. در بخش ۱۱-۳ به ادامه طراحی ATM با شناسایی کلاسها از طریق مستند نیازها ادامه خواهیم داد. این کار را با استخراج اسامی کلیدی و تعبیر اسامی از مستند نیازها انجام خواهیم داد. با استفاده از این کلاسها، اولین پیشنویس خود را از دیاگرام کلاس ایجاد میکنیم که ساختار سیستم ATM را مدلسازی میکند
پاسخ : آموزش ++C مقدمه در فصل دوم، چند برنامه ساده ایجاد کردیم که میتوانستند پیغامهای را به کاربر نشان داده، اطلاعاتی از وی دریافت نمایند، محاسباتی انجام داده و تصمیمگیری کنند. در این فصل، شروع به نوشتن برنامههایی میکنیم که مفاهیم پایه برنامهنویسی شیگرای معرفی شده در بخش ۱۷-۱ را بکار میگیرند. یکی از ویژگیهای مشترک در هر برنامه فصل دوم این است که تمام عبارات که کاری انجام میدهند در تابع main جای داده شدهاند. بطور کلی، برنامههایی که در این کتاب ایجاد میکنید، متشکل از تابع main و یک یا چند کلاس که هر یک حاوی اعضای داده و توابع عضو هستند، خواهند بود. اگر شما عضوی از تیم توسعه (طراحی) در یک مجموعه حرفهای هستید، احتمال دارد بر روی سیستمهای نرمافزاری که حاوی صدها، یا هزاران کلاس است، کار کنید. در این فصل، هدف ما توسعه یک چهارچوب کاری ساده خوشفرم به لحاظ مهندسی نرمافزار به منظور سازماندهی برنامههای شیگرا در C++ است. ابتدا توجه خود را به سمت کلاسهای موجود در دنیای واقعی متمرکز میکنیم. سپس به آرامی به سمت دنبالهای از هفت برنامه کامل میرویم که به توصیف نحوه ایجاد و استفاده از کلاسهای متعلق به خودمان میپردازند. این مثالها با مبحث آموزشی هدفمند که بر توسعه کلاس grade-book تمرکز دارد، شروع میشود و مربی میتواند از آن برای نگهداری امتیازات دانشجو استفاده کند. این مبحث آموزشی در چند فصل آتی بهبود خواهد یافت و در فصل هفتم بحد اعلی خود خواهد رسید. 2-3 کلاسها، شیها، توابع عضو و داده اجازه دهید بحث را با یک مقایسه ساده که در افزایش درک شما از مطالب بخش ۱۷-۱ موثر است آغاز کنیم. فرض کنید که میخواهید با اتومبیلی رانندگی کرده و با فشردن پدال گاز آن را سریعتر به حرکت در آورید. چه اتفاقی قبل از اینکه بتوانید اینکار را انجام دهید، باید رخ دهد؟ بسیار خوب، قبل از اینکه بتوانید با اتومبیلی رانندگی کنید، باید کسی آن را طراحی و ساخته باشد. معمولاً ساخت اتومبیل، با ترسیم یا نقشهکشی مهندسی شروع شود. همانند طراحی صورت گرفته برای خانه. این ترسیمات شامل طراحی پدال گاز است که راننده با استفاده از آن سبب میشود تا اتومبیل سریعتر حرکت کند. تا حدی، پدال سبب «پنهان» شدن پیچیدگی مکانیزمی میشود که اتومبیل را سریعتر بحرکت در میآورد، همانطوری که پدال ترمز سبب «پنهان» شدن مکانیزمی میشود که از سرعت اتومبیل کم میکند، فرمان اتومبیل سبب «پنهان» شدن مکانیزمی میشود که اتومبیل را هدایت میکند و موارد دیگر. با انجام چنین کارهایی، افراد عادی میتوانند به آسانی اتومبیل را هدایت کرده و براحتی از پدال گاز، ترمز و فرمان، مکانیزم تعویض دنده و سایر «واسطهای» کاربرپسند و ساده استفاده کنند تا پیچیدگی مکانیزمهای داخلی اتومبیل برای راننده مشخص نباشد. متاسفانه، نمیتوانید با نقشههای ترسیمی یک اتومبیل رانندگی کنید، قبل از اینکه با اتومبیلی رانندگی کنید باید، آن اتومبیل از روی نقشههای ترسیمی ساخته شود. یک اتومبیل کاملاً ساخته شده دارای پدال گاز واقعی برای به حرکت درآوردن سریع اتومبیل است. اما این هم کافی نیست، اتومبیل بخودی خود شتاب نمیگیرد، از اینرو لازم است راننده بر روی پدال گاز فشار آورد تا به اتومبیل دستور حرکت سریعتر را صادر کند. حال اجازه دهید تا از مثال اتومبیل مطرح شده برای معرفی مفاهیم کلیدی برنامهنویسی شیگرا در این بخش استفاده کنیم. انجام یک وظیفه در یک برنامه مستلزم یک تابع (همانند main که فصل دوم توضیح داده شده) است. تابع، توصیفکننده مکانیزمی است که وظیفه واقعی خود را به انجام میرساند. تابع پیچیدگی وظایفی که قرار است انجام دهد از دید کاربر خود پنهان میسازد، همانند پدال گاز در اتومبیل که پیچیدگی مکانیزم شتابگیری را از دید راننده پنهان میکند. درC++ ، کار را با ایجاد واحدی بنام کلاس که خانه تابع محسوب میشود، آغاز میکنیم، همانند نقشه ترسیمی اتومبیل که طرح پدال گاز نیز در آن قرار دارد. از بخش ۱۷-۱ بخاطر دارید که به تابع متعلق به یک کلاس، تابع عضو گفته میشود. در یک کلاس میتوان یک یا چند تابع عضو داشت که برای انجام وظایف کلاس طراحی شدهاند. برای مثال، یک کلاس میتواند نشاندهنده یک حساب بانکی و حاوی یک تابع عضو برای میزان سپرده در حساب، تابع دیگری برای میزان برداشت پول از حساب و تابع سومی هم برای نمایش میزان پول موجود در حساب باشد. همانطوری که نمیتوانید با نقشه ترسیمی اتومبیل رانندگی کنید، نمیتوانید کلاسی را مشتق کنید. همانطوری که باید قبل از اینکه بتوانید با اتومبیلی رانندگی کنید، شخص از روی نقشه ترسیمی مبادرت به ساخت اتومبیل کرده باشد، شما هم باید قبل از اینکه برنامه بتواند وظایف توصیفی در کلاس را بدست آورد، باید یک شی از کلاس را ایجاد کرده باشید. این یکی از دلایل شناخته شدن C++ بعنوان یک زبان برنامهنویسی شیگرا است. همچنین توجه نمائید که همانطوری که میتوان از روی نقشه ترسیمی اتومبیلهای متعددی ساخت، میتوان از روی یک کلاس، شییهای متعددی ایجاد کرد. زمانیکه رانندگی میکنید، با فشردن پدال گاز، پیغامی به اتومبیل ارسال میشود که وظیفهای را انجام دهد، که این وظیفه افزودن سرعت اتومبیل است. به همین ترتیب، پیغامهایی را به یک شی ارسال میکنیم، هر پیغام بعنوان فراخوان یک تابع عضو شناخته میشود و به تابع عضو از شیی اعلان میکند که وظیفه خود را انجام دهد. اینکار غالباً بعنوان تقاضای سرویس از یک شی شناخته میشود. تا بدینجا، از مقایسه اتومبیل برای توضیح کلاسها، شیها و توابع عضو استفاده کردیم. علاوه بر قابلیتهای اتومبیل، هر اتومبیلی دارای چندین صفت است، صفاتی همانند رنگ، تعداد درها، ظرفیت باک، سرعت جاری و مسافت طی شده. همانند قابلیتهای اتومبیل، این صفات هم بعنوان بخشی از طراحی اتومبیل در نقشه ترسیمی دیده میشوند. همانطوری که رانندگی میکنید، این صفات همیشه در ارتباط با اتومبیل هستند. هر اتومبیلی، حافظ صفات خود است. برای مثال، هر اتومبیلی از میزان سوخت موجود در باک خود مطلع است، اما از میزان سوخت موجود در باک سایر اتومبیلها مطلع نیست. به همین ترتیب، یک شی دارای صفاتی است که به همراه شی بوده و در برنامه بکار گرفته میشوند. این صفات به عنوان بخشی از کلاس شی تصریح میشوند. برای مثال، یک شی حساب بانکی دارای صفت موجودی است که نشاندهنده مقدار پول موجود در حساب میباشد. هر شی حساب بانکی از میزان موجودی در حساب خود مطلع است، اما از موجودی سایر حسابها در بانک اطلاعی ندارد. صفات توسط اعضای داده کلاس مشخص میشوند. ۳-۳ نگاهی بر مثالهای این فصل در مابقی این فصل مبادرت به معرفی هفت مثال ساده میکنیم که به توصیف مفاهیم معرفی شده در ارتباط با قیاس اتومبیل میپردازند. خلاصهای از این مثالها در زیر آورده شده است. ابتدا مبادرت به ایجاد کلاس GradeBook میکنیم تا قادر به توصیف این مفاهیم باشیم: ۱- اولین مثال نشاندهنده کلاس GradeBook با یک تابع عضو است که فقط یک پیغام خوشآمدگویی را در زمان فراخوانی به نمایش در میآورد. سپس شما را با نحوه ایجاد یک شی از این کلاس و فراخوانی تابع عضو که پیغام خوشآمدگویی را ظاهر میسازد، آشنا خواهیم کرد. ۲- دومین مثال، اصلاح شده مثال اول است. در این مثال به تابع عضو اجازه داده میشود تا نام دوره را به عنوان یک آرگومان قبول کند. سپس، تابع عضو مبادرت به نمایش نام دوره بعنوان بخشی از پیغام خوشآمدگویی میکند. ۳- سومین مثال نحوه ذخیرهسازی نام دوره در یک شی GradeBook را نشان میدهد. برای این نسخه از کلاس، شما را با نحوه استفاده از توابع عضو به منظور تنظیم نام دوره در شی و بدست آوردن نام دوره از شی آشنا میکنیم. ۴- چهارمین مثال به توصیف نحوه مقداردهی اولیه داده در شی GradeBook به هنگام ایجاد شی میپردازد. مقداردهی اولیه توسط یک تابع عضو بنام سازنده کلاس صورت میگیرد. همچنین این مثال نشان میدهد که هر شی GradeBook از نام دورة خود نگهداری میکند. ۵- مثال پنجم اصلاح شده مثال چهارم است که به توصیف نحوه قرار دادن کلاس GradeBook در یک فایل مجزا به منظوراستفاده مجدد از نرمافزار، میپردازد. ۶- مثال ششم، تغییریافته مثال پنجم است که حاوی یک اصل مهندسی نرمافزار در جداسازی بخش واسط کلاس از بخش پیادهسازی آن است. با انجام اینکار فرآیند اصلاح و تغییر آسانتر میشود بدون اینکه در سرویسگیرندههای کلاس تاثیرگذار باشد. ۷- آخرین مثال قابلیت کلاس GradeBook را با معرفی اعتبارسنجی داده افزایش خواهد داد. اعتبارسنجی ما را از فرمت صحیح داده در یک شی مطمئن میسازد. برای مثال یک شی Date نیازمند یک مقدار در محدودة ۱ الی ۱۲ است. در این مثال GradeBook، تابع عضوی که مبادرت به تنظیم نام دورة برای یک شی GradeBook میکند، سبب میشود تا نام دوره ۲۵ کاراکتر یا کمتر باشد. اگر طول نام دوره بیش از ۲۵ کاراکتر باشد، تابع عضو فقط از ۲۵ کاراکتر اول استفاده کرده و یک پیغام خطا به نمایش در میآورد. توجه کنید که مثالهای GradeBook در این فصل فرآیند واقعی ذخیرهسازی را انجام نمیدهند. در فصل چهارم، فرآیند پردازش امتیازات را با GradeBook و در فصل هفتم مبادرت به ذخیرهسازی اطلاعات در یک شی GradeBook خواهیم کرد. ۴-۳ تعریف کلاس با یک تابع عضو کار را با مثالی آغاز میکنیم (شکل ۱-۳) که حاوی کلاس GradeBook است و میتوان از آن برای نگهداری امتیازات دانشجویان استفاده کرد. تابع main در خطوط ۲۰-۲۵ مبادرت به ایجاد یک شی GradeBook کرده است. این برنامه نسخه اول بوده و نسخه کامل آن در فصل هفتم ایجاد خواهد شد. تابع main از این شی و تابع عضو آن برای نمایش پیغام بر روی صفحه نمایش (پیغام خوشآمدگویی) استفاده میکند. شکل ۱-۳ | تعریف کلاس GradeBook با یک تابع عضو، ایجاد یک شی GradeBook و فراخوانی تابع عضو آن. ابتدا به توصیف نحوه تعریف یک کلاس و تابع عضو میپردازیم. سپس به توضیح نحوه ایجاد یک شی و نحوه فراخوانی تابع عضو یک شی میپردازیم. چند مثال اول حاوی تابع main و کلاس GradeBook است که از آن در همان فایل استفاده میکند. در انتهای این فصل، به معرفی روشهای خبره در ساختارمند کردن برنامهها به منظور افزایش کارایی و انجام بهتر مهندسی نرمافزار خواهیم پرداخت. کلاس GradeBook قبل از اینکه تابع main (خطوط ۲۰-۲۵) بتواند شی از کلاس GradeBook ایجاد کند، بایستی به کامپایلر توابع عضو و اعضای داده متعلق به کلاس را اعلان کنیم. این کار بنام تعریف کلاس شناخته میشود. تعریف کلاس GradeBook (خطوط ۹-۱۷) حاوی تابع عضوی بنام displayMessage است (خطوط ۱۳-۱۶) که پیغامی بر روی صفحه نمایش چاپ میکند (خط ۱۵). بخاطر دارید که یک کلاس همانند یک نقشه ترسیمی است، از اینرو نیاز داریم که یک شی از کلاس GradeBook ایجاد کنیم (خط ۲۲)، و تابع عضو displayMessage آن را فراخوانی نمایم (خط ۲۳) تا خط ۱۵ را به اجرا درآورده و پیغام خوشآمدگویی را به نمایش درآورد. بزودی به توضیح دقیقتر خطوط ۲۲-۲۳ خواهیم پرداخت. تعریف کلاس از خط ۹ و با کلمه کلیدی class و بدنبال آن نام کلاس GradeBook آغاز شده است. بطور قراردادی، نام کلاسهای تعریف شده توسط کاربر با حروف بزرگ شروع میشوند و در صورت وجود کلمات بعدی، ابتدای آنها هم با حروف بزرگ آغاز میگردد. به این روش غالباً روش camel case میگویند. بدنه هر کلاسی در بین جفت براکت باز و بسته (} و {) محدود میشود، همانند خطوط ۱۰ و۱۷٫ تعریف کلاس با یک سیمکولن خاتمه میپذیرد (خط ۱۷). بخاطر دارید که تابع main همیشه و بصورت اتوماتیک به هنگام اجرای برنامه فراخوانی میشود. اکثر توابع بصورت اتوماتیک فراخوانی نمیشوند. همانطوری که مشاهده خواهید کرد، بایستی تابع عضو displayMessage را بطور صریح فراخوانی کنید تا وظیفه خود را انجام دهد. خط ۱۴ حاوی برچسب تصریحکننده دسترسی public: است. کلمه کلیدی public، تصریحکننده دسترسی نامیده میشود. خطوط ۱۳-۱۶ تعریفکننده تابع عضو displayMessage هستند. این تابع عضو پس از public: قرار دارد تا نشان دهد که تابع «بصورت سراسری در دسترس است»، به این معنی که میتواند توسط سایر توابع موجود در برنامه و توسط توابع عضو کلاسهای دیگر فراخوانی گردد. همیشه در مقابل تصریحکنندههای دسترسی یک کولن (:) قرار داده میشود. برای مابقی متن، زمانیکه به تصریحکننده دسترسی public اشاره میکنیم، کولن آن را در نظر نمیگیریم. در بخش ۶-۳ به معرفی دومین تصریحکننده دسترسی بنام private خواهیم پرداخت. هر تابعی در برنامه وظیفهای را انجام داده و امکان دارد مقداری را پس از کامل کردن وظیفه خود برگشت دهد. برای مثال، تابعی میتواند یک محاسبه انجام داده و سپس نتیجه آن محاسبه را برگشت دهد. زمانیکه تابعی تعریف میشود، باید نوع برگشتی آن را مشخص سازید، تا تابع پس از اتمام کار، آن نوع را برگشت دهد. در خط ۱۳، کلمه کلیدی void قرار گرفته است (در سمت چپ نام تابع displayMessage) و نشاندهندة نوع برگشتی از سوی تابع است. نوع برگشتی void بر این نکته دلالت دارد که displayMessage وظیفهای را انجام خواهد داد، اما دادهای را به فراخوان خود (در این مثال، تابع فراخوان، main میباشد) پس از اتمام وظیفه خود برگشت نخواهد داد. (در شکل ۵-۳ مثالی را مشاهده میکنید که تابع در آن مقداری را برگشت داده است). نام تابع عضو displayMessage بوده و قبل از آن نوع برگشتی قرار دارد. بطور قراردادی، اسامی توابع با یک حرف کوچک شروع شده و تمام کلمات متعاقب آن با حرف بزرگ شروع میشوند. پرانتزهای قرار گرفته پس از نام تابع عضو، بر این نکته دلالت دارند که این یک تابع است. یک جفت پرانتز خالی، همانند خط ۱۳، نشان میدهد که این تابع عضو، نیازی به داده اضافی برای انجام وظیفه خود ندارد. در بخش ۵-۳ شاهد تابع عضوی خواهید بود که برای انجام وظیفه خود نیازمند داده اضافی است. معمولاً به خط ۱۳، سرآیند تابع گفته میشود. بدنه هر تابعی با براکتهای باز و بسته مشخص میشود (} و {)، همانند خطوط ۱۴ و ۱۶٫ بدنه تابع حاوی عباراتی است که وظیفه تابع را به انجام میرسانند. در این مورد، تابع عضو displayMessage حاوی یک عبارت است (خط ۱۵) که پیغام “Welcome to the Grade Book!” را به نمایش در میآورد. پس از اجرای این عبارت، تابع وظیفه خود را به انجام رسانده است. تست کلاس GradeBook در این مرحله میخواهیم که از کلاس GradeBook در برنامه استفاده کنیم. همانطوری که در فصل دوم آموختید، تابع main با اجرای هر برنامهای شروع بکار میکند. خطوط ۲۰-۲۵ از برنامه شکل ۱-۳ حاوی تابع main هستند، که اجرای برنامه را تحت کنترل دارد. در این برنامه، مایل هستیم تابع عضو displayMessage از کلاس GradeBook برای نمایش پیغام خوشآمدگویی اجرا گردد (فراخوانی شود). اصولاً تا زمانیکه شیی از یک کلاس ایجاد نکرده باشید، نمیتوانید تابع عضو کلاس را فراخوانی نمائید (در بخش ۷-۱۰ با تابع عضو static، آشنا خواهید شد که در این مورد یک استثناء میباشد.) خط ۲۲ یک شی از کلاس GradeBook بنام myGradeBookGradeBook است، کلاس تعریف شده در خطوط ۹-۱۷٫ زمانیکه متغیرهایی از نوع int اعلان میکنیم، همانند کاری که در فصل دوم انجام دادیم، کامپایلر از مفهوم intGradeBook اطلاعی ندارد و آن را نمیشناسد، این نوع یک نوع تعریف شده توسط کاربر است. از اینرو، باید به کامپایلر، با قرار دادن تعریف کلاس همانند کاری که در خطوط ۹-۱۷ انجام دادهایم، GradeBook را معرفی کنیم. اگر این خطوط را حذف کنیم، کامپایلر پیغام خطایی همانند ” ‘GradeBook’ : undeclared identifier” را در GNU C++ صادر میکند. هر کلاس جدیدی که ایجاد میکنید، تبدیل به یک نوع جدید میشود که میتواند برای ایجاد شیها بکار گرفته شود. برنامهنویسان میتوانند در صورت نیاز مبادرت به تعریف کلاسهای با نوع جدید نمایند، و این یکی از دلایل شناخته شدن C++ بعنوان یک زبان بسطپذیر است. ایجاد میکند. خط ۲۳ مبادرت به فراخوانی تابع عضو displayMessage (تعریف شده در خطوط ۱۳-۱۶) با استفاده از متغیر myGradeBook و بدنبال آن عملگر نقطه (.)، سپس نام تابع displayMessage و یک جفت پرانتز خالی میکند. این فراخوانی موجب میشود که تابع displayMessage برای انجام وظیفه خود فراخوانی شود. در ابتدای خط ۲۳، عبارت “myGradeBook.” بر این نکته دلالت دارد که main بایستی از شی GradeBook که در خط ۲۲ ایجاد شده است، استفاده نماید. پرانتزهای خالی در خط ۱۳ نشان میدهند که تابع عضو displayMessage نیازی به داده اضافی برای انجام وظیفه خود ندارد. (در بخش ۵-۳، با نحوه ارسال داده به تابع آشنا خواهید شد) زمانیکه displayMessage وظیفه خود را انجام داد، تابع main به اجرای خود از خط ۲۴ ادامه خواهد داد، که نشان میدهد main وظیفه خود را به بدرستی انجام داده است. با رسیدن به انتهای main، برنامه خاتمه میپذیرد. دیاگرام کلاس UML برای کلاس GradeBook از بخش ۱۷-۱ بخاطر دارید که UML یک زبان گرافیکی بکار رفته توسط برنامهنویس برای نمایش سیستمهای شیگرا به یک روش استاندارد است. در UML، هر کلاس در یک دیاگرام کلاس و بصورت یک مستطیل با سه قسمت (بخش) مدلسازی میشود. شکل ۲-۳ نشاندهنده یک دیاگرام کلاس UML برای کلاس GradeBook معرفی شده در برنامه ۱-۳ است. بخش فوقانی حاوی نام کلاس است، که در وسط قرار گرفته و بصورت توپر نوشته میشود. بخش میانی، حاوی صفات کلاس است که مرتبط با اعضای داده در C++ است. در شکل ۲-۳ بخش میانی خالی است، چرا که این نسخه از کلاس GradeBook در برنامه ۱-۳ دارای صفات نیست. (در بخش ۶-۳ نسخهای از کلاس GradeBook عرضه شده که دارای یک صفت است.) بخش تحتانی حاوی عملیات کلاس است، که متناظر با توابع عضو در C++ میباشد. شکل ۲-۳ | دیاگرام کلاس UML نشان میدهد که کلاس GradeBook دارای یک عملیات سراسری بنام displayMessage است. UML مبادرت به مدلسازی عملیاتها با لیست کردن نام عملیات و بدنبال آن مجموعه پرانتزها میکند. کلاس GradeBook فقط دارای یک تابع عضو بنام displayMessage است، از اینرو بخش تحتانی در شکل ۲-۳ فقط یک عملیات با این نام را لیست کرده است. تابع عضو displayMessage برای انجام وظیفه خود نیازی به اطلاعات اضافی ندارد، از اینرو پرانتزهای قرار گرفته پس از displayMessage در این دیاگرام کلاس خالی هستند، همانند سرآیند تابع عضو قرار گرفته در خط ۱۳ برنامه۱-۳٫ علامت جمع (+) که قبل از نام عملیات آورده شده، نشان میدهد که displayMessage یک عملیات سراسری در UML است (یک تابع عضو public در C++). به دفعات از دیاگرامهای کلاس UML برای خلاصه کردن صفات و عملیات کلاسها استفاده خواهیم کرد. ۵-۳ تعریف تابع عضو با پارامتر در مثال قیاس اتومبیل در بخش ۲-۳ خاطرنشان کردیم که فشردن پدال گاز سبب ارسال پیغامی به اتومبیل میشود تا وظیفهای را به انجام برساند، که در این مورد افزودن سرعت اتومبیل است. اما اتومبیل باید چقدر سرعت بگیرد؟ همانطوری که میدانید، با فشردن هر چه بیشتر پدال، سرعت اتومبیل افزایش پیدا میکند. بنابر این پیغام ارسالی به اتومبیل هم حاوی وظیفه بوده و همچنین حاوی اطلاعات دیگری است که به اتومبیل در انجام وظیفه کمک میکند. این اطلاعات اضافی بنام پارامتر شناخته میشوند، مقدار پارامتر به اتومبیل کمک میکند تا میزان افزایش سرعت را تعیین کند. به همین ترتیب، یک تابع عضو میتواند نیازمند یک یا چندین پارامتر بعنوان داده اضافی برای انجام وظیفه خود باشد. فراخوان تابع مقادیری بنام آرگومان، برای هر پارامتر تابع تدارک میبیند. برای مثال، در سپردهگذاری در حساب بانکی، فرض کنید تابع عضو deposit از کلاس Account پارامتری که نشاندهنده مقدار سپرده است، مشخص کرده باشد. زمانیکه تابع عضو deposit فراخوانی شود، مقدار آرگومان که نشاندهنده مقدار سپرده است به پارامتر تابع عضو کپی میشود. سپس تابع عضو این مقدار سپرده را به میزان موجودی اضافه میکند.
پاسخ : آموزش ++C تعریف و تست کلاس GradeBook مثال بعدی (برنامه شکل ۳-۳) تعریف مجددی از کلاس GradeBook (خطوط ۱۴-۲۳) با تابع عضو displayMessage است (خطوط ۱۸-۲۲) که نام دوره را بعنوان بخشی از رشته خوشآمدگویی چاپ میکند. تابع عضو جدید displayMessage مستلزم یک پارامتر است (courseName در خط ۱۸) که نشاندهنده نام دوره است. قبل از اینکه به بررسی ویژگیهای جدید کلاس GradeBook بپردازیم، اجازه دهید به نحوه استفاده از کلاس جدید در main نگاهی داشته باشیم (خطوط ۲۶-۴۰). در خط ۲۸ یک متغیر از نوع رشته (string) بنام nameOfCourse ایجاد شده است که از آن برای ذخیره کردن نام دوره وارد شده توسط کاربر استفاده خواهیم کرد. متغیر از نوع string نشاندهنده رشتهای از کاراکترها همانند “CS101 Introduction to C++ Programming” است. نوع string در واقع شی از کلاس string کتابخانه استاندارد C++ است. این کلاس در فایل سرآیند <string> تعریف شده و نام string همانند cout، متعلق به فضاینامی std است. برای اینکه خط ۲۸ قادر به کامپایل شدن باشد، خط ۹ حاوی سرآیند <string> است. دقت کنید که اعلان using در خط ۱۰ به ما اجازه میدهد تا به آسانی در خط ۲۸ از string بجای std::string استفاده کنیم. برای این لحظه، میتوانید در مورد متغیرهای string همانند متغیرهای از نوع دیگر همانند int فکر کنید. در بخش ۱۰-۳ با قابلیتهای string بیشتر آشنا خواهید شد. خط ۲۹ یک شی از کلاس GradeBook بنام myGradeBook ایجاد میکند. خط ۳۲ به کاربر اعلان میکند که نام دوره را وارد سازد. خط ۳۳ مبادرت به خواندن نام دوره از ورودی کاربر کرده و آن را با استفاده از تابع کتابخانهای getline به متغیر nameOfCourse تخصیص میدهد. قبل از اینکه به توضیح این خط از کد بپردازیم، اجازه دهید تا توضیح دهیم چرا نمیتوانیم برای بدست آوردن نام دوره فقط بنویسیم cin>>nameOfCourse; در اجرای نمونه این برنامه، ما از نام دوره “CS101 Introduction to C++ Programming” استفاده کردهایم که حاوی چندین کلمه است. زمانیکه cin با عملگر استخراج به کار گرفته میشود، مبادرت به خواندن کاراکترها تا رسیدن به اولین کاراکتر white-space میکند. از اینرو فقط “CS101″ توسط این عبارت خوانده میشود و برای خواندن مابقی نام دوره مجبور به عملیات ورودی اضافی خواهیم بود. در این مثال، مایل هستیم تا کاربر نام کامل دوره را تایپ کرده و کلید Enter را برای تحویل آن به برنامه فشار دهد، و کل نام دوره در متغیر رشتهای nameOfCourse ذخیره گردد فراخوانی تابع getline(cin, nameOfCourse) در خط ۳۳، سبب میشود که کاراکترها (از جمله کاراکترهای فاصله که کلمات را از هم در ورودی جدا میکنند) از شی ورودی استاندارد جریان cin (صفحه کلید) تا رسیدن به کاراکتر خط جدید خوانده شده و در متغیر رشتهای nameOfCourse جای داده شده و کاراکتر خط جدید حذف میگردد. توجه نمائید زمانیکه کلید Enter فشرده میشود (در زمان تایپ ورودی)، یک خط جدید (newline) به جریان ورودی افزوده میشود. همچنین توجه کنید که فایل سرآیند <string> باید قرار داشته باشد تا بتوان از تابع getline استفاده کرد. نام این تابع متعلق به فضای نامی std است. شکل ۳-۳ | تعریف کلاس GradeBook با تابع عضوی که پارامتر دریافت میکند. خط ۳۸ تابع عضو displayMessage شی myGradeBook را فراخوانی میکند. متغیرnameOfCource موجود در درون پرانتزها آرگومانی است که به تابع displayMessage ارسال میشود، از اینروست که تابع میتواند وظیفه خود را به انجام رساند. مقدار متغیر nameOfCourse در main تبدیل به مقدار پارامتر courseName تابع عضو displayMessage در خط ۱۸ میشود. زمانیکه این برنامه اجرا شود، توجه نمائید که تابع عضو displayMessage به همراه پیغام خوشآمدگویی، نام دورة تایپ شده توسط کاربر را به نمایش درمیآورد. آرگومانها و پارامترهای بیشتر برای تصریح اینکه تابعی نیازمند داده برای انجام وظایف خویش است، اطلاعات اضافی را در لیست پارامتری تابع قرار میدهیم، لیستی که در درون پرانتزها قرار گرفته پس از نام تابع جای دارد. لیست پارامترها میتواند به هر تعداد پارامتر داشته باشد یا اصلاً پارامتری نداشته باشد (همانند پرانتزهای خالی در خط ۱۳ برنامه شکل ۱-۳) تا نشان دهد تابع نیاز به هیچ پارامتری ندارد. لیست پارامتری تابع عضو displayMessage به نحوی اعلان شده که تابع نیازمند یک پارامتر است (شکل ۳-۳، خط ۱۸). هر پارامتر باید دارای نوع و یک شناسه باشد. در این مورد، نوع string بوده و شناسه courseName است و نشان میدهد که تابع عضو displayMessage مستلزم یک رشته برای انجام وظیفه خویش است. بدنه تابع عضو از پارامتر courseName برای دسترسی به مقدار ارسالی به تابع در فراخوانی تابع (خط ۳۸ در main) استفاده میکند. خطوط ۲۰-۲۱ مقدار پارامتر courseName را بعنوان بخشی از پیغام خوشآمدگویی به نمایش در میآورد. توجه نمائید که نام متغیر پارامتری (خط ۱۸) میتواند همنام متغیر آرگومان (خط ۳۸) یا نام متفاوتی داشته باشد. در فصل ششم با دلایل اینکارها آشنا خواهید شد. یک تابع میتواند دارای چندین پارامتر باشد، بشرطی که هر پارامتر از دیگری با یک ویرگول مجزا شده باشد (در مثال شکلهای ۴-۶ و ۵-۶ شاهد آنها خواهید بود). تعداد و ترتیب آرگومانها در یک تابع فراخوانی شده بایستی با تعداد و ترتیب لیست پارامترها در سرآیند تابع عضو فراخوانی شده مطابقت داشته باشد. همچنین، نوع آرگومانها در تابع فراخوانی شده باید با نوع پارامترهای متناظر در سرآیند تابع مطابقت داشته باشد. (همانطوری که جلوتر میرویم، شاهد خواهید بود که نوع یک آرگومان میتواند همانند نوع پارامتر متناظر خود نباشد.) در مثال ما، یک آرگومان رشتهای در تابع فراخوانی شده وجود دارد (nameOfCourse) که دقیقاً با پارامتری رشتهای در تعریف تابع عضو مطابقت دارد (CourseName). به روز کردن دیاگرام کلاس UML برای کلاس GradeBook دیاگرام کلاس UML در شکل ۴-۳ مبادرت به مدلسازی کلاس GradeBook از برنامه ۳-۳ کرده است. همانند کلاس GradeBook تعریف شده در برنامه ۱-۳، این کلاس GradeBook حاوی یک تابع عضو سراسری displayMessage است. با این وجود، این نسخه از displayMessage دارای یک پارامتر میباشد. UML پارامتر را با لیست کردن نام پارامتر، بدنبال آن یک کولن و سپس نوع پارامتر در درون پرانتزهای قرار گرفته پس از نام عملیات مدلسازی میکند. UML دارای نوع دادههای متعلق بخود شبیه C++ است. UML یک زبان مستقل بوده و از آن در کنار انواع زبانهای برنامهنویسی استفاده میشود، از اینرو اصطلاحات تخصصی آن دقیقاً مطابق با C++ نیست. برای مثال، نوع رشته (String) در UML متناظر با نوع string در C++ است. تابع عضو displayMessage از کلاس GradeBook (شکل ۳-۳ خطوط ۱۸-۲۲) دارای یک پارامتر رشتهای بنام CourseName است، از اینرو در شکل ۴-۳ بصورت courseName : String در میان پرانتزها و پس از نام diaplayName، قرار گرفته است. دقت کنید که این نسخه از کلاس GradeBook هنوز دارای اعضای داده نیست. شکل ۴-۳ | دیاگرام کلاس UML نشان میدهد که کلاس GradeBook دارای عملیات displayMessage با یک پارامتر از نوع String در UML است. ۶-۳ اعضای داده، توابع set و get در فصل دوم، تمام متغیرهای برنامه را در تابعmain اعلان کردیم. متغیرهای اعلان شده در بدنه تعریف یک تابع، بنام متغیرهای محلی شناخته میشوند و میتوانند فقط از خطی که در آن تابع اعلان شدهاند تا رسیدن به براکت بستن سمت راست (}) در تعریف تابع بکار گرفته شوند. قبل از اینکه بتوان از یک متغیر محلی استفاده کرد، باید ابتدا آن را اعلان کرده باشید. زمانیکه تابع به کار خود خاتمه میدهد، مقادیر متغیرهای محلی آن از بین میروند (در فصل ششم با متغیرهای محلی static آشنا خواهید شد که از این قاعده مستثنی هستند). از بخش ۲-۳ بخاطر دارید که شیی که دارای صفت است، آن را با خود همراه دارد و در برنامه از آن استفاده میشود. چنین صفاتی در کل طول عمر یک شی وجود خواهند داشت. معمولاً کلاسی که دارای یک یا چندین تابع عضو است، مبادرت به دستکاری کردن صفات متعلق به یک شی مشخص از کلاسی میکند. صفات به عنوان متغیرهای در تعریف کلاس عرضه میشوند. چنین متغیرهای، بنام اعضای داده شناخته میشوند و در درون تعریف کلاس اعلان میگردند اما در خارج از تعریف بدنه تابع عضو کلاس جای میگیرند. هر شی از کلاس مسئول نگهداری کپی از صفات متعلق به خود در حافظه است. مثال مطرح شده در این بخش به توصیف کلاس GradeBook میپردازد که حاوی یک عضو داده CourseName است تا نشاندهنده نام یک دوره خاص از شی GradeBook باشد. کلاس GradeBook با یک عضو داده، یک تابع set و get در این مثال، کلاس GradeBook (برنامه شکل ۵-۳) مبادرت به نگهداری نام دوره بصورت یک عضو داده میکند و از اینروست که میتواند بکار گرفته شده یا در هر زمان اجرای برنامه آن را اصلاح کرد. کلاس حاوی توابع عضو setCourseName، getCourseName و displayMessage است. تابع عضو setCourseName نام دوره را در عضو داده GradeBook ذخیره میسازد. تابع عضو getCourseName نام دوره را از عضو داده بدست میآورد. تابع عضو displayMessage که هیچ پارامتری ندارد، پیغام خوشآمدگویی را که شامل نام دوره است، به نمایش در میآورد. با این همه، همانطوری که مشاهده میکنید، در حال حاضر تابع، نام دوره را با فراخوانی تابع دیگری در همان کلاس بدست میآورد، تابع getCourseName. شکل ۵-۳ | تعریف و تست کلاس GradeBook با یک عضو داده و توابع set و get. معمولاً یک مدرس بیش از یک دوره را تدریس میکند که هر دوره دارای نام متعلق بخود است. در خط ۳۹، متغیر CourseName از نوع رشته اعلان شده است. بدلیل اینکه متغیر اعلان شده در درون تعریف کلاس اعلان شده (خطوط ۱۵-۴۰) اما در خارج از بدنه تعاریف توابع عضو جای دارد (خطوط ۱۹-۲۲، ۲۵-۲۸ و ۳۱-۳۷)، خط ۳۹ اعلانی برای یک عضو داده است. هر نمونهای (شی) از کلاس GradeBookGradeBook وجود داشته باشد، هر شی دارای یک کپی از CourseName متعلق بخود خواهد بود، همانطوری که در مثال برنامه شکل ۷-۳ شاهد آن خواهید بود. مزیت ایجاد CourseName بصورت یک عضو داده در این است که تمام توابع عضو یک کلاس (در این مورد،GradBook ) میتوانند در هر عضو داده موجود که در تعریف کلاس وجود دارد، دستکاری کنند (در این مورد، CourseName). حاوی یک کپی از هر عضو داده کلاس خواهد بود. برای مثال، اگر دو شی تصریحکننده دسترسی public و private اکثر اعلانهای اعضای داده پس از برچسب تصریحکننده دسترسی private: ظاهر میشوند (خط ۳۸). همانند public، کلمه کلیدی private یک تصریحکننده است. متغیرها یا توابع اعلان شده پس از private (و قبل از تصریحکننده دسترسی بعدی) فقط در توابع عضو کلاسی که در آن اعلان شدهاند، در دسترس خواهند بود. بنابر این، عضو داده CourseName فقط میتواند در توابع عضو setCourseName، getCourseName و displayMessage از کلاس GradeBook بکار گرفته شود. بدلیل اینکه عضو داده CourseName بصورت private اعلان شده است، نمیتواند توسط توابع خارج از کلاس (همانند main) یا توابع عضو کلاسهای دیگر در برنامه بکار گرفته شود. مبادرت به دسترسی عضو داده courseName در یکی از نقاط این برنامه با عبارتی همانند myGradeBook.courseName خطای کامپایل تولید کرده و پیغامی مشابه cannot access private member declared in class ‘GradeBook’ به نمایش در میآید. توابع عضو setCourseName و getCourseName تابع عضو setCourseName تعریف شده در خطوط ۱۹-۲۲ به هنگام اتمام وظیفه خود هیچ مقداری برگشت نمیدهد و از اینرو نوع برگشتی آن void است. تابع عضو یک پارامتر دریافت میکند، name، که نشاندهنده نام دورهای است که به آن بعنوان یک آرگومان ارسال خواهد شد (خط ۵۵ از main). خط ۲۱ مبادرت به تخصیص name به عضو داده courseName میکند. در این مثال، setCourseName مبادرت به اعتبارسنجی نام دوره نمیکند (بررسی فرمت نام دوره با یک الگوی خاص). برای نمونه فرض کنید که دانشگاهی میتواند از اسامی دوره تا ۲۵ کاراکتر یا کمتر چاپ بگیرد. در این مورد، مایل هستیم کلاس GradeBook ما را مطمئن سازد که عضو داده courseName هرگز بیش از ۲۵ کاراکتر نداشته باشد. در بخش ۱۰-۳ با تکنیکهای اولیه اعتبارسنجی آشنا خواهید شد. تابع عضو getCourseName تعریف شده در خطوط ۲۵-۲۸ یک شی خاص از GradeBook بنام courseName را برگشت میدهد. تابع عضو دارای یک لیست پارامتری خالی است و از اینرو برای انجام وظیفه خود نیازی به اطلاعات اضافی ندارد. تابع مشخص میسازد که یک رشته برگشت خواهد داد. زمانیکه تابعی که نوع برگشتی آن بجز void است فراخوانی میشود و وظیفه خود را انجام میدهد، تابع نتیجهای را به فراخوان خود برگشت میدهد. برای مثال، هنگامی که به سراغ یک ATM میروید و تقاضای نمایش میزان موجودی خود را میکنید، انتظار دارید ATM مقداری که نشاندهنده میزان موجودی است به شما برگشت دهد. به همین ترتیب، هنگامی که عبارتی تابع عضو getCourseName از یک شی GradeBook را فراخوانی میکند، عبارت انتظار دریافت نام دوره را دارد (در این مورد، یک رشته است که توسط نوع برگشتی تابع مشخص گردیده است). اگر تابعی بنام square داشته باشید که مربع آرگومان خود را برگشت میدهد، عبارت int results = square(2); مقدار ۴ را از تابع square برگشت داده و متغیر result را با ۴ مقداردهی اولیه میکند. اگر تابعی بنام maximum داشته باشید که بزرگترین عدد را از بین سه آرگومان خود برگشت میدهد، عبارت int biggest = maximum(27,114,41); عدد ۱۱۴ را از تابع maximum برگشت داده و متغیر biggest با عدد ۱۱۴ مقداردهی اولیه میشود. توجه کنید که عبارات قرار گرفته در خطوط ۲۱ و ۲۷ هر یک از متغیر courseName (خط ۳۹) استفاده کردهاند، بدون اینکه این متغیر در توابع عضو اعلان شده باشد میتوانیم از courseName در توابع عضو کلاس GradeBookاستفاده کنیم چرا که courseName یک عضو داده از کلاس است. همچنین توجه نمائید که ترتیب تعریف توابع عضو تعیینکننده ترتیب فراخوانی در زمان اجرا نیستند از اینرو تابع عضو getCourseName میتواند قبل از تابع عضو setCourseName تعریف شود. تابع عضو displayMessage تابع عضو displayMessage در خطوط ۳۱-۳۷ هیچ نوع دادهای را پس از کامل کردن وظیفه خود برگشت نمیدهد، از اینروست که نوع برگشتی آن void تعریف شده است. تابع، پارامتری دریافت نمیکند، بنابر این لیست پارامتری آن خالی است. خطوط ۳۵-۳۶ پیغام خوشآمدگویی را که حاوی مقدار عضو داده courseName است در خروجی چاپ میکنند. خط ۳۵ مبادرت به فراخوانی تابع getCourseNamecourseName میکند. توجه نمائید که تابع عضو displayMessage هم قادر به دسترسی مستقیم به عضو داده courseName است. در مورد اینکه چرا از فراخوانی تابع عضو getCourseName برای بدست آوردن مقدار courseName استفاد کردهایم
پاسخ : آموزش ++C تست کلاس GradeBook تابع main (خطوط ۴۳-۶۰) یک شی از کلاس GradeBook ایجاد کرده و از توابع عضو آن استفاده میکند. در خط ۴۶ یک شی GradeBook بنام myGradeBook ایجاد شده است. خطوط ۴۹-۵۰ نام دوره اولیه را با فراخوانی تابع عضو getCourseName به نمایش در میآورند. توجه کنید که اولین خط خروجی نشاندهنده نام دوره نمیباشد، چرا که عضو داده courseName در ابتدای کار تهی است. بطور پیشفرض، مقدار اولیه یک رشته، رشته تهی است، رشتهای که حاوی هیچ کاراکتری نمیباشد. زمانیکه یک رشته تهی به نمایش درآید، چیزی بر روی صفحه نمایش ظاهر نمیشود. خط ۵۳ از کاربر میخواهد که نام دوره را وارد سازد. متغیر محلی nameOfCourse اعلان شده در خط ۴۵، با نام دوره وارد شده توسط کاربر مقداردهی میشود که از فراخوانی تابع getline بدست میآید (خط ۵۴). خط ۵۵ تابع عضو setCourseName را فراخوانی کرده و nameOfCourse را بعنوان آرگومان تابع بکار میگیرد. به هنگام فراخوانی تابع، مقدار آرگومان به پارامتر name (خط ۱۹) از تابع عضو setCourseName کپی میگردد (خطوط ۱۹-۲۲). سپس مقدار پارامتر به عضو داده courseName تخصیص مییابد (خط ۲۱). خط ۵۷ یک خط خالی در خروجی قرار داده، سپس خط ۵۸ تابع عضو displayMessage را برای نمایش پیغام خوشآمدگویی به همراه نام دوره فراخوانی میکند. مهندسی نرمافزار با توابع set و get اعضای داده private یک کلاس فقط قادر به دستکاری شدن از طریق توابع عضو آن کلاس هستند (و «دوستان» آن کلاس که در فصل دهم با آنها آشنا خواهید شد). بنابر این سرویسگیرنده یک شی، (یعنی هر کلاس یا تابعی که مبادرت به فراخوانی توابع عضو از خارج شی میکند)، توابع عضو public کلاس را برای دریافت سرویسهای کلاس برای شیهای خاصی از کلاس فراخوانی میکند. به همین دلیل است که عبارات موجود در تابع main (برنامه شکل ۵-۳، خطوط ۴۳-۶۰) مبادرت به فراخوانی توابع عضو setCourseName، getCourseName و displayMessage موجود در شی GradeBookpublic کلاسها به سرویسگیرندهها اجازه تخصیص مقادیر به (set) یا دریافت مقادیر از (get) اعضای داده private را میدهند. نیازی نیست که اسامی این توابع عضو حتماً با set یا get شروع شوند، اما این روش نامگذاری مرسوم است. در این مثال، تابع عضوی که مبادرت به تنظیم (تخصیص مقادیر) عضو داده courseName میکند، setCourseName نام دارد و تابع عضوی که مقداری از عضو داده courseName بدست میآورد، تابع getCourseName نامیده میشود. توجه کنید که گاهاً توابع set، بنام mutators شناخته میشوند (چرا که مبادرت به تغییر یا اصلاح مقادیر میکنند)، و توابع get بنام accessors نامیده میشوند (چرا که به مقادیر دسترسی پیدا میکنند). میکند. غالباً توابع عضو بخاطر دارید که اعلان اعضای داده با تصریحکننده دسترسی private موجب پنهانسازی داده میشود. تدارک دیدن توابع set و get به سرویسگیرندههای کلاس اجازه دسترسی به دادههای پنهان شده را میدهند، اما بصورت غیرمستقیم. سرویسگیرنده از مبادرت خود به اصلاح یا بدست آوردن داده یک شی اطلاع دارد، اما از اینکه شی چگونه این عملیات را انجام میدهد، اطلاعی ندارد. در برخی از موارد امکان دارد کلاسی به یک روش مبادرت به عرضه داده در محیط داخلی نماید، در حالیکه همان داده را به روش مختلفی در اختیار سرویسگیرندهها قرار میدهد. برای مثال، فرض کنید کلاس Clock زمانی از روز را بصورت یک عضو داده time و از نوع int و private عرضه میکند که تعداد ثانیههای از نیمهشب را ذخیره میسازد. با این همه، زمانیکه سرویسگیرندهای تابع عضو getTime از شی Clock را فراخوانی میکند، زمان برحسب ساعت، دقیقه و ثانیه در یک رشته با فرمت “HH:MM:SS” به وی برگشت داده میشود. به همین ترتیب، فرض کنید کلاس Clock یک تابع set بنام setTime تدارک دیده که یک رشته پارامتری با فرمت “HH:MM:SS” دریافت مینماید. با استفاده از قابلیتهای عرضه شده در فصل هیجدهم، تابع setTime قادر به تبدیل این رشته به تعداد ثانیهها است، که تابع آن را در عضو داده private ذخیره سازد. همچنین تابع set میتواند به بررسی اعتبار مقدار دریافتی پرداخته و اعتبار آن را تایید یا لغو کند (مثلاً “۱۲:۳۰:۴۳″ معتبر بوده اما “۴۲:۸۵:۷۰″ معتبر نیست). توابع set و get به سرویسگیرنده امکان تعامل با یک شی را فراهم میآورند، اما داده private (خصوصی) شی همچنان بصورت کپسوله (پنهان) و ایمن در خودش نگهداری میشود. همچنین توابع set و get یک کلاس میتوانند توسط سایر توابع عضو موجود در درون کلاس به منظور دستکاری کردن داده private کلاس بکار گرفته شوند، اگر چه این توابع عضو میتواند بصورت مستقیم به داده private دسترسی پیدا کنند. در برنامه شکل ۵-۳، توابع عضو getCourseName و setCourseName از نوع توابع عضو public هستند، از اینرو در دسترسگیرندههای کلاس و همچنین خود کلاس قرار دارند. تابع عضو displayMessage تابع عضو getCourseName را برای بدست آوردن مقدار عضو داده courseName برای نمایش آن، فراخوانی میکند، با اینکه خود displayMessage میتواند بصورت مستقیم به courseName دسترسی پیدا کند. دسترسی به عضو داده از طریق تابع get آن، سبب ایجاد یک کلاس بهتر و پایدارتر میشود (کلاسی که نگهداری آن آسان بوده و کمتر از کار میافتد). اگر تصمیم به تغییر عضو داده courseName بگیریم، ساختار تعریفکننده displayMessage نیازمند اصلاح نخواهد بود، فقط بدنه توابع get و set که مستقیماً عضو داده را دستکاری میکنند نیاز به تغییر خواهند داشت. برای مثال، فرض کنید که میخواهیم نام دوره را در دو عضو داده عرضه کنیم، courseName (مثلاً “CS101″) و courseTitle (مثلاً “Introduction to C++ Programming”). هنوز هم تابع displayMessage میتواند با یک فراخوانی تابع عضو getCourseName نام کامل دوره را بدست آورده و بعنوان بخشی از پیغام خوشآمدگویی به نمایش در آورد. در اینحالت، تابع getCourseName نیازمند ایجاد و برگشت دادن یک رشته حاویcourseNumber و بدنبال آن courseTitle است. در ادامه تابع عضو displayMessage عنوان کامل دوره “CS101 Introduction to C++ Programming” را به نمایش در خواهد آورد، چرا که از تغییرات صورت گرفته بر روی اعضای داده کلاس به دور مانده است. مزایای فراخوانی تابع set از یک تابع عضو دیگر کلاس به هنگام بحث اعتبارسنجی در بخش ۱۰-۳ بیشتر آشکار خواهد شد. دیاگرام UML کلاس GradeBook با یک داده عضو و توابع set و get شکل ۶-۳ حاوی دیاگرام کلاس UML به روز شده برای نسخهای از کلاس GradeBook بکار رفته در برنامه ۵-۳ است. این دیاگرام مبادرت به مدلسازی داده عضو courseName بعنوان یک صفت در بخش میانی کلاس کرده است. UML اعضای داده را در لیستی که در آن نام صفت، یک کولن و نوع صفت قرار گرفته عرضه میکند. نوع UML صفت courseName، نوع String است که متناظر با string در C++ میباشد. عضو داده courseName در C++ حالت private دارد و از اینرو در دیاگرام کلاس با یک علامت منفی (-) در مقابل نام صفت مشخص شده است. علامت منفی در UML معادل با تصریحکننده دسترسی private در C++ است. کلاس GradeBook حاوی سه تابع عضو public است، از اینرو در لیست دیاگرام کلاس این سه عملیات در بخش تحتانی یا سوم جای گرفتهاند. بخاطر دارید که نماد جمع (+) قبل نام هر عملیات نشان میدهد که عملیات در C++ حالت public دارد. عملیات setCourseName دارای یک پارامتر String بنام name است. در UML نوع برگشتی از یک عملیات با قرار دادن یک کولن و نوع برگشتی پس از پرانتزهای نام عملیات مشخص میشود. تابع عضو getCourseName از کلاس GradeBook (شکل ۵-۳) دارای نوع string برگشتی در C++ است، بنابر این دیاگرام کلاس نوع برگشتی String را در UML به نمایش درآورده است. توجه نمائید که عملیاتهای setCourseName و displayMessage مقداری برگشت نمیدهند (نوع برگشتی آنها void است)، از اینرو در دیاگرام کلاس UML نوع برگشتی از پرانتزها برای آنها در نظر گرفته نشده است. UML از void همانند C++ در زمانیکه تابع مقداری برگشت نمیدهد، استفاده نمیکند. شکل ۶-۳ | دیاگرام کلاس UML برای کلاس GradeBook با یک صفت private برای courseName و عملیاتهای public برای توابع setCourseName، getCourseName و displayMessage 7-3 مقداردهی اولیه شیها با سازندهها همانطوری که از بخش ۶-۳ بخاطر دارید، زمانیکه یک شی از کلاس GradeBook ایجاد شد (شکل ۵-۳) عضو داده آن یعنی courseName بطور پیشفرض با یک رشته تهی مقداردهی اولیه شده بود. اگر بخواهیم به هنگام ایجاد یک شی از GradeBook نام دورهای تدارک دیده شود، چه کاری باید انجام داد؟ هر کلاسی که اعلان میکنید میتواند یک سازنده (constructor) داشته باشد که میتوان با استفاده از آن مبادرت به مقداردهی اولیه یک شی از کلاس به هنگام ایجاد شی کرد. سازنده یک تابع عضو ویژه است که بایستی همنام با نام کلاس تعریف شده باشد، از اینروست که کامپایلر میتواند آن را از دیگر توابع عضو کلاس تشخیص دهد. مهمترین تفاوت موجود مابین سازندهها و توابع دیگر در این است که سازندهها نمیتواند مقدار برگشت دهند، بنابر این نمیتوانند نوع برگشتی داشته باشند (حتی void). معمولاً، سازندهها بصورت public اعلان میشود. غالباً کلمه “constructor” در برخی از نوشتهها بصورت خلاصه شده “ctor” بکار گرفته میشود، که استفاده از آن را ترجیح ندادهایم. C++ نیازمند فراخوانی یک سازنده برای هر شی است که ایجاد میشود، در چنین حالتی مطمئن خواهیم بود که شی قبل از اینکه توسط برنامه بکار گرفته شود، بدرستی مقداردهی اولیه شده است. فراخوانی سازنده به هنگام ایجاد شی، بصورت غیرصریح یا ضمنی انجام میشود. در هر کلاسی که بصورت صریح سازندهای را مشخص نکرده است، کامپایلر یک سازنده پیشفرض تدارک میبیند، این سازنده دارای پارامتر نمیباشد. برای مثال، زمانیکه خط ۴۶ از برنامه شکل ۵-۳ یک شی GradeBook ایجاد میکند، سازنده پیشفرض فراخوانی میشود، چرا که در اعلان myGradeBook بطور صریح هیچ آرگومان سازندهای مشخص نشده است. سازنده پیشفرض تدارک دیده شده از سوی کامپایلر یک شی GradeBook بدون هیچ گونه مقادیر اولیه برای اعضای داده شی ایجاد میکند. [نکته: برای اعضای داده که شیهای از سایر کلاسها هستند، سازنده پیشفرض بصورت ضمنی هر سازنده پیشفرض عضو داده را برای اطمینان از اینکه اعضاء داده به درستی مقداردهی اولیه شدهاند، فراخوانی میکند. در واقع به این دلیل است که عضو داده courseName از نوع رشته (در برنامه شکل ۵-۳) با یک رشته تهی مقداردهی اولیه شده است. سازنده پیشفرض برای کلاس string مبادرت به تنظیم یک مقدار رشتهای با رشته تهی میکند، در بخش ۳-۱۰ مطالب بیشتری در ارتباط با مقداردهی اولیه اعضای داده که شیهای از کلاسهای دیگر هستند خواهید آموخت.] در مثال برنامه شکل ۷-۳، نام یک دوره را به هنگام ایجاد یک شی از GradeBook مشخص کردهایم (خط ۴۹). در این مورد، آرگومان “CS101 Introduction to C++ Programming” به سازنده شی GradeBook ارسال میشود (خطوط ۱۷-۲۰) و مبادرت به مقداردهی اولیه courseName مینماید. برنامه شکل ۷-۳ یک کلاس GradeBook اصلاح شده تعریف کرده که حاوی یک سازنده با یک پارامتر رشتهای است که نام دورة اولیه را دریافت میکند. شکل ۷-۳ | نمونهسازی شیهای مضاعف از کلاس GradeBook و استفاده از سازنده GradeBook برای مشخص کردن نام دوره به هنگام ایجاد هر شی GradeBook.
پاسخ : آموزش ++C تعریف سازنده در خطوط ۱۷-۲۰ برنامه شکل ۷-۳ یک سازنده برای کلاس GradeBook تعریف شده است. توجه کنید که سازنده دارای نام مشابه همانند کلاس خود یعنی GradeBook است. یک سازنده توسط لیست پارامتری، داده مورد نیاز برای انجام وظیفه خود را مشخص میسازد. زمانیکه یک شی جدید ایجاد میکنید، این داده را در درون پرانتزهای قرار گرفته پس از نام شی قرار میدهید (همانند خطوط ۴۹-۵۰). خط ۱۷ بر این نکته دلالت دارد که سازنده کلاس GradeBook دارای یک پارامتر رشتهای بنام name است. دقت کنید که در خط ۱۷ نوع برگشتی مشخص نشده است، چرا که سازندهها نمیتوانند مقدار برگشت دهند (حتی void). خط ۱۹ در بدنه سازنده مبادرت به ارسال پارامتر name سازنده به تابع عضو setCourseName میکند که مقداری به عضو داده courseName تخصیص میدهد. تابع عضو setCourseName (خطوط ۲۳-۲۶) فقط مبادرت به تخصیص پارامتر name خود به عضو داده courseName میکند، بنابر این ممکن است تعجب کنید که چرا زحمت فراخوانی setCourseName را در خط ۱۹ به خود دادهایم، در حالیکه سازنده بطور مشخص عملیات تخصیص courseName=name را انجام میدهد. در بخش ۱۰-۳، مبادرت به اصلاح setCourseName برای انجام عملیات اعتبارسنجی خواهیم کرد. در این بخش است که مزیت فراخوانی setCourseName از سازنده آشکار خواهد شد. توجه نمائید که در سازنده (خط ۱۷) و هم تابع setCourseName (خط ۲۳) از پارامتری بنام name استفاده شده است. میتوانید از اسامی پارامتری یکسان در توابع مختلف استفاده کنید چرا که پارامترها حالت محلی برای هر تابع دارند و سبب تداخل با دیگری نمیشوند. تست کلاس GradeBook خطوط ۴۶-۵۷ از برنامه شکل ۷-۳ حاوی تعریف تابع main است که مبادرت به تست کلاس GradeBook و اثبات مقداردهی اولیه شی GradeBook با استفاده از یک سازنده میکند. خط ۴۹ در تابع main اقدام به ایجاد و مقداردهی اولیه یک شی GradeBook بنام gradeBook1 میکند. زمانی که این خط از کد اجرا شود، سازنده GradeBook در خطوط ۱۷-۲۰ همراه با آرگومان “CS101 Introduction to C++ Programming” برای مقداردهی اولیه نام دوره gradeBook1 فراخوانی میشود، البته این فراخوانی بصورت ضمنی و توسط C++ صورت میگیرد. خط ۵۰ تکرار این فرآیند برای یک شی GradeBook بنام gradeBook2 است، این بار، آرگومان “CS102 Data Structures in C++” برای مقداردهی اولیه نام دوره gradeBook2 ارسال میشود. خطوط ۵۳-۵۴ از تابع عضو هر شی getCourseName برای بدست آوردن اسامی دوره و نمایش اینکه آنها در زمان ایجاد مقداردهی اولیه شدهاند، استفاده کردهاند. خروجی برنامه تایید میکند که هر شی GradeBook از کپی عضو دادهcourseName متعلق بخود نگهداری کرده است. دو روش برای تدارک دیدن یک سازنده پیشفرض برای یک کلاس به هر سازندهای که هیچ آرگومانی دریافت نکند، سازنده پیشفرض میگویند. یک کلاس به یکی از دو روش زیر سازندة پیشفرض بدست میآورد: ۱- کامپایلر بطور ضمنی یک سازنده پیشفرض برای کلاسی که سازندهای برای آن تعریف نشده است، ایجاد میکند. چنین سازندهای مبادرت به مقداردهی اولیه اعضای داده کلاس نمیکند، اما برای هر عضو داده که شی از کلاس دیگری هستند، سازنده پیشفرض را فراخوانی میکند [نکته: معمولاً یک متغیر مقداردهی نشده حاوی یک مقدار «اشغال» است (مثلاً یک متغیر int مقداردهی نشده میتواند حاوی ۸۵۸۹۹۳۴۶۰- باشد که این مقدار در بسیاری از برنامهها برای این متغیر غلط میباشد).] ۲- برنامهنویس بصورت صریح مبادرت به تعریف سازندهای نماید که آرگومانی دریافت نمیکند. چنین سازندهای شروع به مقداردهی اولیه مطابق نظر برنامهنویس کرده و سازنده پیشفرض را برای هر داده عضو که شی از یک کلاس دیگر است فراخوانی میکند. اگر برنامهنویس مبادرت به تعریف یک سازنده با آرگومان نماید، دیگر C++ بصورت ضمنی یک سازنده پیشفرض برای آن کلاس ایجاد نخواهد کرد. توجه نمائید که برای هر نسخه از کلاس GradeBook در برنامههای ۱-۳، ۳-۳ و ۵-۳ کامپایلر بصورت ضمنی یک سازنده پیشفرض تعریف کرده است. افزودن سازنده به دیاگرام کلاس UML کلاس GradeBook دیاگرام کلاس UML در شکل ۸-۳ مبادرت به مدل کردن کلاس GradeBook برنامه ۷-۳ کرده است، که دارای یک سازنده با پارامتر name از نوع رشته است (نوع String در UML). همانند عملیاتها، UML مبادرت به مدلسازی سازندهها در بخش سوم کلاس در دیاگرام کلاس میکند. برای تمایز قائل شدن مابین یک سازنده از یک عملیات کلاس، UML مبادرت به قرار دادن کلمه “constructor” مابین گیومه (« و ») قبل از نام سازنده میکند. قرار دادن لیست سازنده کلاس قبل از دیگر عملیاتها در بخش سوم امری رایج است. 8-3 قرار دادن کلاس در یک فایل مجزا برای استفاده مجدد تا آنجا که از منظر برنامهنویسی نیاز داشته باشیم به توسعه کلاس GradeBook ادامه خواهیم داد، از اینرو اجازه دهید تا وارد برخی از مباحث مهندسی نرمافزار شویم. یکی از مزایای تعریف دقیق یک کلاس در این است که به هنگام بسته (package) کردن صحیح، کلاسهای ما میتوانند توسط سایر برنامهنویسان در سرتاسر جهان بکار گرفته شوند (استفاده مجدد). برای مثال، کتابخانه استاندارد C++ دارای نوع string است که میتوانیم از آن در هر برنامه C++ استفاده کنیم (استفاده مجدد). البته این کار را با وارد کردن فایل سرآیند <string> در برنامه انجام میدهیم. متاسفانه، برنامهنویسانی که مایل به استفاده از کلاس GradeBook ما هستند، نمیتواند بسادگی و فقط با وارد کردن فایل از برنامه ۷-۳ به یک برنامه دیگر از آن استفاده کنند. همانطوری که در فصل دوم آموختید، تابع main اجرای هر برنامهای را آغاز میکند و هر برنامه باید دارای یک تابع main باشد. اگر برنامهنویسان دیگر مبادرت به قرار دادن کد برنامه ۷-۳ نمایند، چمدان بزرگی بدست خواهند گرفت، تابع main ما، و برنامه آنها حاوی دو تابع main خواهد بود. زمانیکه مبادرت به کامپایل برنامه کنند، کامپایلر متوجه خطا خواهد شد، چرا که هر برنامهای فقط میتواند یک تابع main داشته باشد. برای مثال، اگر مبادرت به کامپایل برنامهای با دو تابع main در برنامه Mircrosoft Visual C++ .NET کنید، خطای زیر تولید میشود: error C2084: function ‘int main(void)’ already has a body هنگامی که کامپایلر سعی در کامپایل دومین تابع main میکند با خطا مواجه میشود. به همین ترتیب کامپایلر GNU C++ خطای زیر را تولید میکند: redefinition of ‘int main()’ این خطاها بر این نکته دلالت دارند که برنامه در حال حاضر دارای یک تابع main است، بنابر این، قرار دادن main در همان فایل با تعریف کلاس از اینکه بتوان از کلاس در سایر برنامهها استفاده مجدد کرد، ممانعت بعمل میآورد. در این بخش، به توضیح نحوه ایجاد کلاس GradeBook با هدف استفاده مجدد خواهیم پرداخت. روشی که در آن کلاس را در یک فایل مجزا از تابع main قرار میدهیم. فایلهای سرآیند هر کدام یک از مثالهای مطرح شده تا بدین مرحله متشکل از یک فایل .cpp بودند که بعنوان فایل کد-منبع نیز شناخته میشوند. این مثالها حاوی تعریف کلاس GradeBook و یک تابع main بودند. به هنگام ایجاد یک برنامه شیگرای C++، تعریف کد منبع با قابلیت استفاده مجدد (همانند یک کلاس) در یک فایل که دارای پسوند فایل .h (فایل سرآیند) است، امری رایج میباشد. در برنامهها از رهنمودهای پیشپردازنده #include برای وارد کردن فایلهای سرآیند و برخوردار شدن از مزیت کامپونتهای نرمافزاری با قابلیت استفاده مجدد کمک گرفته میشود، همانند نوع string تدارک دیده شده در کتابخانه استاندارد C++ و نوعهای تعریف شده توسط کاربر مانند کلاس GradeBook. در مثال بعدی، مبادرت به متمایز کردن کد برنامه از ۷-۳ با دو فایلGradeBook.h برنامه شکل ۹-۳ و fig03_10.cpp برنامه شکل ۱۰-۳ میکنیم. همانطوری که در فایل سرآیند شکل ۹-۳ مشاهده میکنید، این فایل فقط حاوی تعریف کلاس GradeBook (خطوط ۱۱-۴۱) و خطوط ۳-۸ است که به کلاس GradeBook اجازه استفاده از endl ، cout و نوع string را میدهند. تابع main که از کلاس GradeBook استفاده میکند در فایل کد منبع fig03_10.cpp شکل ۱۰-۳ تعریف شده است، در خطوط ۱۰-۲۱٫ برای کمک به شما برای مهیا شدن برای کار با برنامههای بزرگ که در این کتاب و بازار کار با آنها مواجه خواهید شد، غالباً از یک فایل کد منبع متمایز یا جداگانه حاوی تابع main برای تست کلاسهای خود استفاده میکنیم (به این برنامه، برنامه راهانداز یا درایور میگویند). بزودی با نحوه استفاده یک فایل کد منبع با main از تعریف کلاس موجود در یک فایل سرآیند در ایجاد شیهای از یک کلاس آشنا خواهید شد. شکل ۹-۳ | تعریف کلاس .GradeBook وارد کردن فایل سرآیند که حاوی یک کلاس تعریف شده از سوی کاربر است یک فایل سرآیند همانند GradeBook.h (برنامه شکل ۹-۳) نمیتواند برای شروع اجرای برنامه بکار گرفته شود، چرا که حاوی تابع main نمیباشد. اگر سعی در کامپایل و لینک خود GradeBook.h به منظور ایجاد یک برنامه اجرایی کنید، Microsoft Visual C++ .NET پیغام خطای لینکر را تولید خواهد کرد: error LNK2019:unresolved external symbol _main referenced in function _mainCRTStrartup اگر در حال استفاده از GNU C++ بر روی لینوکس باشید، پیغام خطای لینکر بصورت زیر خواهد بود: undefined reference to ‘main’ این خطا بر این نکته دلالت دارد که لینکر قادر به یافتن تابع main برنامه نشده است. برای تست کلاس GradeBook تعریف شده در شکل ۹-۳ بایستی یک فایل کد منبع جداگانه حاوی یک تابع main (همانند شکل ۱۰-۳) بنویسید که مبادرت به نمونهسازی و استفاده از شیهای کلاس کند. از بخش ۴-۳ بخاطر دارید که کامپایلر از مفهوم نوعهای بنیادین همانند int مطلع است، اما با GradeBook آشنا نیست چرا که این نوع یک نوع تعریف شده از سوی کاربر (برنامهنویس) است. در واقع، کامپایلر حتی از کلاسهای کتابخانه استاندارد C++ اطلاعی ندارد. برای کمک به کامپایلر برای اینکه متوجه نحوه استفاده از یک کلاس شود، بایستی بصورت صریح تعریف کلاس را در اختیار آن قرار دهیم. به همین دلیل است که برای مثال، به هنگام استفاده از نوع string، باید برنامه شامل فایل سرآیند <string> باشد. با انجام اینکار، کامپایلر قادر به تعیین میزان حافظهای خواهد بود که باید برای هر شی از کلاس رزرو نماید و فراخوانی صحیح توابع عضو کلاس را تضمین میکند. شکل ۱۰-۳ | وارد کردن کلاس GradeBook از فایل GradeBook.h برای استفاده در main. برای ایجاد شیهای gradeBook1 و gradeBook2 در خطوط ۱۳-۱۴ برنامه ۱۰-۳، باید کامپایلر از سایز یک شی GradeBook مطلع باشد. در حالیکه شیها بصورت مفهومی حاوی اعضای داده و توابع عضو هستند، شیهای C++ فقط حاوی داده میباشند. کامپایلر فقط یک کپی از توابع عضو کلاس ایجاد کرده و آن کپی را در میان سایر شیهای کلاس به اشتراک میگذارد. البته هر شی نیازمند کپی متعلق بخود از اعضای داده کلاس است، چرا که محتویات آنها میتوانند با سایر شیها بسیار تفاوت داشته باشند (همانند دو شی مختلف BankAccount (حساب بانکی) که دو عضو داده متفاوت balance یعنی موجودی دارند). با این وجود، کد تابع عضو تغییر پذیر نیست، از اینرو میتواند در میان تمام شیهای کلاس به اشتراک گذاشته شود. بنابر این، سایز یک شی وابسته به میزان فضای حافظه مورد نیاز برای ذخیرهسازی عضوهای داده کلاس است. با وارد کردن GradeBook.h در خط ۷، به کامپایلر امکان دسترسی به اطلاعات مورد نیاز (شکل ۹-۳، خط ۴۰) برای تعیین سایز یک شی GradeBook و تعیین اینکه آیا شیهای از آن کلاس بدرستی بکار گرفته شدهاند یا خیر، داده میشود (در خطوط ۱۳-۱۴ و ۱۷-۱۸ شکل ۱۰-۳). خط ۷ به پیشپردازنده C++ دستور جایگزین رهنمود با یک کپی از محتویات GradeBook.h (تعریف کلاس GradeBook) قبل از کامپایل برنامه را صادر میکند. زمانیکه فایل کد منبع fig03_10.cpp کامپایل میشود، حاوی تعریف کلاس GradeBook بوده (بدلیل #include)، و کامپایلر قادر به تعیین نحوه ایجاد شیهای GradeBook و مشاهده فراخوانی صحیح توابع عضو خواهد بود. اکنون که تعریف کلاس در یک فایل سرآیند (بدون تابع main) قرار دارد، میتوانیم سرآیند را در هر برنامهای که نیاز به استفاده مجدد از کلاس GradeBook دارد وارد کنیم.