• توجه: در صورتی که از کاربران قدیمی ایران انجمن هستید و امکان ورود به سایت را ندارید، میتوانید با آیدی altin_admin@ در تلگرام تماس حاصل نمایید.

آموزش ++C

c_cpp.jpg
یکی از نویسنده‌گان این کتاب، پورفسور 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++ بکار رفته بر روی سیستم خود مراجعه کنید.

شکل ۱-۱ |
cpp-ch1-1.png
.
دو ویرایشگر که در سیستم UNIX کاربرد بیشتری دارند عبارتند از vi و emacs. بسته‌های نرم‌افزاری C++ برای ویندوز میکروسافت همانند
Metroworks CodeWarrior (http://www.metrowerks. com), Borland C++ (http://www.borland. com)

و Microsoft Visual C++ (http://www.msdn.microsoft.com/visualc/) دارای ویرایشگر مجتمع در محیط برنامه‌نویسی خود هستند. البته می‌توانید از یک ویرایشگر متنی ساده همانند 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++ بدون رعایت هیچ‌گونه ساختار و روش مشخص شروع به برنامه‌نویسی می‌کنند, که چنین کاری در برنامه‌نویسی روش ضعیفی است. خواندن, نگهداری, تست و خطایابی چنین برنامه‌هایی بسیار مشکل بوده و گاهاً رفتار عجیبی از خود بروز می‌دهند. این کتاب با در نظر گرفتن برنامه‌نویسان مبتدی سعی در نوشتن واضح برنامه‌ها دارد.
 
مفاهیم پایه تکنولوژی شی
مقدمه بحث شی‌گرا را با برخی اصطلاحات کلیدی آغاز می‌کنیم. به هر کجا در دنیای واقعی نگاه کنید. شاهد شی‌ها خواهید بود. مردم، حیوانات، گیاهان، اتومبیل‌ها، هواپیماها، ساختمان‌ها، کامپیوترها و بسیاری از چیزهای دیگر. تفکر انسان براساس شی است. تلفن‌ها، خانه‌ها، چراغ‌های ترافیک، اجاق‌های میکروویو و کولرهای آبی تعدادی کمی از بی‌شمار شی‌ها هستند که در زندگی روزمره خود شاهد آنها هستیم.
گاهی اوقات مبادرت به تقسیم شی‌ها به دو طبقه می‌کنیم: متحرک و غیرمتحرک. شی‌های متحرک، زنده هستند و می‌توانند حرکت کرده و کاری انجام ‌دهند. از سوی دیگر، شی‌های غیرمتحرک، در محیط خود حرکت نمی‌کنند. با این همه، شی‌ها از هر دو نوع، در برخی چیزها مشترک هستند. همه آنها دارای صفات (همانند سایز، شکل، رنگ و وزن) بوده و از خود رفتارهای نشان می‌دهند (مثلاً توپ می‌غلتد، بالا و پایین می‌پرد، یک بچه گریه می‌کند، می‌خوابد، راه می‌رود، یک اتومبیل شتاب می‌گیرد، ترمز کرده و چرخش می‌کند، یک حوله آب را جذب می‌کند).
انسان‌ها با بررسی صفات و رفتار اشیاء، مطالبی در ارتباط با آنها یاد می‌گیرند. شی‌های مختلف می‌توانند صفات و رفتار مشابهی داشته باشند. مقایسه را همیشه می‌توان اعمال کرد، مثلاً مابین کودکان و بزرگسالان.



طراحی شی‌گرا یک روش طبیعی و ذاتی در نمایش فرآیند طراحی نرم‌افزار بنام مدلسازی شی‌ها توسط صفات، رفتار و رابطه موجود در میان شی‌ها همانند شی‌ها در دنیای واقعی است. همچنین 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 (به آدرس http://www.omg.org) یک سازمان غیرتجاری است که مبادرت به نشر تکنولوژی‌های استاندارد شی‌گرا همانند 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++ است. برای آشنایی بهتر شما، به توضیح خط به خط برنامه می‌پردازیم.
در خطوط ۱ و ۲
// Fig. 2.1: fig02_01.cpp
// Text-printing program.

که با //، آغاز شده‌اند، نشان‌دهنده این مطلب هستند که این خطوط توضیح می‌باشند. برنامه‌نویسان توضیحات را در برنامه یا لیست کد قرار می‌دهند تا خوانایی کدهای خود را افزایش دهند. توضیحات می‌توانند در خط متعلق به خود جای داده شوند که آنها را توضیحات تمام خط می‌نامیم یا در انتهای یک خط از کد که آنها را توضیحات انتهای خط می‌نامیم، قرار بگیرند. کامپایلرC++ توضیحات را نادیده می‌گیرد، به این معنی که توضیحات هیچ تأثیری در اجرای برنامه ندارند. توضیح بکار رفته در خط ۱ فقط نشان‌دهنده شماره تصویر و نام فایل این برنامه است. البته برخی از برنامه‌نویسان عبارات توضیحی خود را مابین کاراکترهای /* و */ قرار می‌دهند.

cpp-ch2-1.png

شکل ۱-۲ | برنامه چاپ عبارت.
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 مواجه شود، کرسر به ابتدای خط بعدی منتقل خواهد شد. برای ایجاد یک خط خالی در خروجی، دو کاراکتر خط جدید را پشت سرهم، همانند خط ۸ قرار دهید.

cpp-ch2-3.png

شکل ۳-۲ | چاپ متن در یک خط با چند عبارت.
cpp-ch2-4.png

شکل ۴-۲ | چاپ متن در چند خط با یک عبارت.
 
یک برنامه ساده دیگر: جمع اعداد صحیح

در برنامه بعدی از شی جریان ورودی std::cin و عملگر استخراج >>، برای بدست آوردن دو عدد صحیح تایپ شده از سوی کاربر از طریق صفحه‌کلید، جمع آنها و نمایش نتیجه بدست آمده با استفاده از std::cout می‌پردازیم. برنامه شکل ۵-۲ عمل جمع و خروجی حاصل از این برنامه را نشان می‌دهد.


cpp-ch2-5.png

شکل ۵-۲ | برنامه جمع که مجموع دو عدد صحیح دریافتی از صفحه کلید را محاسبه می‌کند.
توضیحات موجود در خطوط ۱ و ۲

// 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 همچنان بدون تغییر باقی می‌ماند. با اینکه از این مقادیر استفاده شده است، اما مقادیر اولیه آنها با انجام عمل محاسباتی از بین نمی‌رود.

cpp-ch2-6.png

شکل ۶-۲ | مکان حافظه در حال نمایش نام و مقدار متغیر number1.
cpp-ch2-7.png

شکل ۷-۲ | مکان‌های حافظه پس از ذخیره‌سازی مقادیر برای number1 و number2.

cpp-ch2-8.png

شکل ۸-۲| مکان‌های حافظه پس از محاسبه sum با استفاده از number1 و number2.
 
محاسبات

اکثر برنامه‌ها محاسبات ریاضی انجام می‌دهند. عملگرهای ریاضی در جدول شکل ۹-۲ لیست شده‌اند. توجه کنید که تمام نمادهای بکار رفته در جبر در 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)، خروجی مرتبط با آن عبارت به اجرا درخواهد آمد. برنامه شکل ۱۳-۲ نشاندهنده برنامه و کادرهای ورودی و خروجی از اجرای نمونه برنامه است.




cpp-ch2-13.png

شکل ۱۳-۲ | عملگرهای رابطه‌ای و مقایسه‌ای.
در خطوط ۶-۸
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” ) و ثابت‌ها (همانند عدد ۱۰۰۰ ) بر روی چند خط خطای نحوی خواهد بود.

در جدول شکل ۱۴-۲ تقدم عملگرهای معرفی شده در این فصل بطور یکجا آورده شده‌اند. اولویت عملگرها از بالا به پایین کاهش می‌یابد. دقت کنید که تمام این عملگرها بجر از عملگر تخصیص =، از سمت چپ به راست ارزیابی می‌شوند.

عملگر شرکت‌پذیری نوع ( ) چپ به راست پرانتز * / % چپ به راست ضرب، تقسیم،باقیمانده + - چپ به راست جمع، تفریق << >> چپ به راست ورود و خروج داده < <= > >= چپ به راست رابطه‌ای == != چپ به راست مقایسه‌ای = راست به چپ تخصیصی
شکل ۱۴-۲ تقدم عملگرهای معرفی شده تا بدین مرحله.

 
مبحث آموزشی مهندسی نرم‌افزار: بررسی نیازمندی‌های 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 را مدل‌سازی می‌کند.
 
مقدمه
در فصل دوم، چند برنامه ساده ایجاد کردیم که می‌توانستند پیغام‌های را به کاربر نشان داده، اطلاعاتی از وی دریافت نمایند، محاسباتی انجام داده و تصمیم‌گیری کنند. در این فصل، شروع به نوشتن برنامه‌هایی می‌کنیم که مفاهیم پایه برنامه‌نویسی شی‌گرای معرفی شده در بخش ۱۷-۱ را بکار می‌گیرند. یکی از ویژگی‌های مشترک در هر برنامه فصل دوم این است که تمام عبارات که کاری انجام می‌دهند در تابع 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 از این شی و تابع عضو آن برای نمایش پیغام بر روی صفحه نمایش (پیغام ‌خوش‌آمدگویی) استفاده می‌کند.
cpp-ch3-1.png


شکل ۱-۳ | تعریف کلاس 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++ می‌باشد.
cpp-ch3-2.png

شکل ۲-۳ | دیاگرام کلاس UML نشان می‌دهد که کلاس GradeBook دارای یک عملیات سراسری بنام displayMessage است.
UML مبادرت به مدلسازی عملیات‌ها با لیست کردن نام عملیات و بدنبال آن مجموعه پرانتزها می‌کند. کلاس GradeBook فقط دارای یک تابع عضو بنام displayMessage است، از اینرو بخش تحتانی در شکل ۲-۳ فقط یک عملیات با این نام را لیست کرده است. تابع عضو displayMessage برای انجام وظیفه خود نیازی به اطلاعات اضافی ندارد، از اینرو پرانتزهای قرار گرفته پس از displayMessage در این دیاگرام کلاس خالی هستند، همانند سرآیند تابع عضو قرار گرفته در خط ۱۳ برنامه۱-۳٫ علامت جمع (+) که قبل از نام عملیات آورده شده، نشان می‌دهد که displayMessage یک عملیات سراسری در UML است (یک تابع عضو public در ‍C++). به دفعات از دیاگرام‌های کلاس UML برای خلاصه کردن صفات و عملیات کلاس‌ها استفاده خواهیم کرد.
۵-۳ تعریف تابع عضو با پارامتر

در مثال قیاس اتومبیل در بخش ۲-۳ خاطرنشان کردیم که فشردن پدال گاز سبب ارسال پیغامی به اتومبیل می‌شود تا وظیفه‌ای را به انجام برساند، که در این مورد افزودن سرعت اتومبیل است. اما اتومبیل باید چقدر سرعت بگیرد؟ همانطوری که می‌دانید، با فشردن هر چه بیشتر پدال، سرعت اتومبیل افزایش پیدا می‌کند. بنابر این پیغام ارسالی به اتومبیل هم حاوی وظیفه بوده و همچنین حاوی اطلاعات دیگری است که به اتومبیل در انجام وظیفه کمک می‌کند. این اطلاعات اضافی بنام پارامتر شناخته می‌شوند، مقدار پارامتر به اتومبیل کمک می‌کند تا میزان افزایش سرعت را تعیین کند. به همین ترتیب، یک تابع عضو می‌تواند نیازمند یک یا چندین پارامتر بعنوان داده اضافی برای انجام وظیفه خود باشد. فراخوان تابع مقادیری بنام آرگومان، برای هر پارامتر تابع تدارک می‌بیند. برای مثال، در سپرده‌گذاری در حساب بانکی، فرض کنید تابع عضو deposit از کلاس Account پارامتری که نشاندهنده مقدار سپرده است، مشخص کرده باشد. زمانیکه تابع عضو deposit فراخوانی شود، مقدار آرگومان که نشاندهنده مقدار سپرده است به پارامتر تابع عضو کپی می‌شود. سپس تابع عضو این مقدار سپرده را به میزان موجودی اضافه می‌کند.
 
تعریف و تست کلاس 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 است.
cpp-ch3-31.png


cpp-ch3-32.png


شکل ۳-۳ | تعریف کلاس 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 هنوز دارای اعضای داده نیست.
cpp-ch3-4.png

شکل ۴-۳ | دیاگرام کلاس UML نشان می‌دهد که کلاس GradeBook دارای عملیات displayMessage با یک پارامتر از نوع String در UML است.
۶-۳ اعضای داده، توابع set و get

در فصل دوم، تمام متغیرهای برنامه را در تابعmain اعلان کردیم. متغیرهای اعلان شده در بدنه تعریف یک تابع، بنام متغیرهای محلی شناخته می‌شوند و می‌توانند فقط از خطی که در آن تابع اعلان شده‌اند تا رسیدن به براکت بستن سمت راست (}) در تعریف تابع بکار گرفته شوند. قبل از اینکه بتوان از یک متغیر محلی استفاده کرد، باید ابتدا آن را اعلان کرده باشید. زمانیکه تابع به کار خود خاتمه می‌دهد، مقادیر متغیرهای محلی آن از بین می‌روند (در فصل ششم با متغیرهای محلی static آشنا خواهید شد که از این قاعده مستثنی هستند). از بخش ۲-۳ بخاطر دارید که شیی که دارای صفت است، آن را با خود همراه دارد و در برنامه از آن استفاده می‌شود. چنین صفاتی در کل طول عمر یک شی وجود خواهند داشت.

معمولاً کلاسی که دارای یک یا چندین تابع عضو است، مبادرت به دستکاری کردن صفات متعلق به یک شی مشخص از کلاسی می‌کند. صفات به عنوان متغیرهای در تعریف کلاس عرضه می‌شوند. چنین متغیرهای، بنام اعضای داده شناخته می‌شوند و در درون تعریف کلاس اعلان می‌گردند اما در خارج از تعریف بدنه تابع عضو کلاس جای می‌گیرند. هر شی از کلاس مسئول نگهداری کپی از صفات متعلق به خود در حافظه است. مثال مطرح شده در این بخش به توصیف کلاس GradeBook می‌پردازد که حاوی یک عضو داده CourseName است تا نشان‌دهنده نام یک دوره خاص از شی GradeBook باشد.
کلاس GradeBook با یک عضو داده، یک تابع set و get

در این مثال، کلاس GradeBook (برنامه شکل ۵-۳) مبادرت به نگهداری نام دوره بصورت یک عضو داده می‌کند و از اینروست که می‌تواند بکار گرفته شده یا در هر زمان اجرای برنامه آن را اصلاح کرد. کلاس حاوی توابع عضو setCourseName، getCourseName و displayMessage است. تابع عضو setCourseName نام دوره را در عضو داده GradeBook ذخیره می‌سازد. تابع عضو getCourseName نام دوره را از عضو داده بدست می‌آورد. تابع عضو displayMessage که هیچ پارامتری ندارد، پیغام خوش‌آمدگویی را که شامل نام دوره است، به نمایش در می‌آورد. با این همه، همانطوری که مشاهده می‌کنید، در حال حاضر تابع، نام دوره را با فراخوانی تابع دیگری در همان کلاس بدست می‌آورد، تابع getCourseName.
cpp-ch3-51.png


cpp-ch3-52.png


شکل ۵-۳ | تعریف و تست کلاس 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 استفاد کرده‌ایم
 
تست کلاس 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++ در زمانیکه تابع مقداری برگشت نمی‌دهد، استفاده نمی‌کند.
cpp-ch3-6.png

شکل ۶-۳ | دیاگرام کلاس 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 اصلاح شده تعریف کرده که حاوی یک سازنده با یک پارامتر رشته‌ای است که نام دورة اولیه را دریافت می‌کند.
cpp-ch3-71.png

cpp-ch3-72.png

شکل ۷-۳ | نمونه‌سازی شی‌های مضاعف از کلاس GradeBook و استفاده از سازنده GradeBook برای مشخص کردن نام دوره به هنگام ایجاد هر شی GradeBook.
 
تعریف سازنده

در خطوط ۱۷-۲۰ برنامه شکل ۷-۳ یک سازنده برای کلاس 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 از تعریف کلاس موجود در یک فایل سرآیند در ایجاد شی‌های از یک کلاس آشنا خواهید شد.
cpp-ch3-91.jpg


cpp-ch3-92.jpg


شکل ۹-۳ | تعریف کلاس .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> باشد. با انجام اینکار، کامپایلر قادر به تعیین میزان حافظه‌ای خواهد بود که باید برای هر شی از کلاس رزرو نماید و فراخوانی صحیح توابع عضو کلاس را تضمین می‌کند.

cpp-ch3-10.jpg


شکل ۱۰-۳ | وارد کردن کلاس 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 دارد وارد کنیم.
 
نحوه یافتن فایل‌های سرآیند

توجه کنید که نام فایل سرآیند GradeBook.h در خط ۷ شکل ۱۰-۳ در میان جفت گوتیشن ( ) بجای < > محدود شده است. معمولاً فایلهای کد منبع برنامه و فایل‌های سرآیند تعریف شده از سوی کاربر در همان شاخه قرار داده می‌شوند. زمانیکه پیش‌پردازنده با یک نام فایل سرآیند در میان گوتیشن‌ها مواجه می‌شود (مثل “GradeBook.h”)، مبادرت به مکان‌یابی فایل سرآیند در همان شاخه می‌کند که فایل حاوی #include در آن قرار دارد. اگر پیش‌پردازنده موفق به یافتن فایل سرآیند در آن شاخه نشود، شروع به جستجوی آن فایل در همان موقعیت‌(ها) همانند فایل‌های سرآیند کتابخانه استاندارد C++ می‌کند. زمانیکه پیش‌پردازنده با نام فایل سرآیند در میان < > مواجه شود (مثل <iostream>) فرض می‌کند که سرآیند بخشی از کتابخانه استاندارد C++ است و به شاخه‌ای که برنامه در آن قرار دارد نگاه نمی‌کند.



مبحث مهندسی نرم‌افزار

اکنون که کلاس GradeBook در یک فایل سرآیند تعریف شده است، کلاس از قابلیت استفاده مجدد برخوردار شده است. متاسفانه، قرار دادن تعریف یک کلاس در یک فایل سرآیند همانند شکل ۹-۳ هنوز هم کل ساختار پیاده‌سازی کلاس را در دید سرویس‌گیرندگان کلاس قرار می‌دهد. GradeBook.h یک فایل متنی ساده است که هر کسی می‌تواند آن را باز کرده و بخواند. اصول مهندسی نرم‌افزار بر این نکته تاکید دارد که به هنگام استفاده از یک شی از کلاسی، کد سرویس‌گیرنده فقط نیاز به فراخوانی توابع عضو مورد نیاز، اطلاع داشتن از تعداد آرگومان‌ها در هر تابع عضو و نوع برگشتی هر تابع عضو دارد. نیازی نیست که کد سرویس‌گیرنده از نحوه پیاده‌سازی توابع مطلع باشد.

اگر کد سرویس‌گیرنده از نحوه پیاده‌سازی یک کلاس مطلع باشد، امکان دارد برنامه‌نویس کد سرویس‌گیرنده براساس جزئیات ساختار پیاده‌سازی کلاس مبادرت به برنامه‌نویسی کد سرویس‌گیرنده کند. ایده‌آل نخواهد بود، اگر در پیاده‌سازی تغییری رخ دهد، سرویس‌گیرنده کلاس مجبور به تغییر در کد خود نباشد. با پنهان‌سازی جزئیات پیاده‌سازی کلاس، کار تغییر در ساختار کلاس آسانتر شده و احتمال تغییر در کد سرویس‌گیرنده‌های کلاس به حداقل می‌رسد. در بخش ۹-۳ شما را با نحوه تفکیک کلاس GradeBook به دو فایل آشنا خواهیم کرد که با اینکار
۱- کلاس قابلیت استفاده مجدد پیدا می‌کند
۲- سرویس‌ گیرنده‌های کلاس از توابع عضو تدارک دیده شده توسط کلاس، نحوه فراخوانی و نوع برگشتی از آنها مطلع می‌شوند
۳- سرویس گیرنده‌ها از نحوه پیاده‌سازی توابع عضو کلاس مطلع نخواهند بود.
۹-۳ جداسازی واسط از پیاده‌سازی

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

واسط‌ها تعریف‌کننده روش‌های استاندارد در برقراری تعامل چیزهای همانند مردم و سیستم‌ها با یکدیگر هستند. برای‌مثال، کنترل‌های(دکمه‌های) رادیو نقش واسط مابین کاربران رادیو و کامپونت‌های داخلی‌‌ آن بازی می‌کنند. کنترل‌ها به کاربران امکان انجام کارهای مشخصی را می‌دهند، کارهای همانند تغییر ایستگاه، تنظیم صدا و انتخاب ایستگاه‌های FM و AM. امکان دارد رادیوهای مختلف این عملیات‌ها را به روش‌های متفاوتی انجام دهند، برخی از دکمه‌های فشاری، برخی از صفات شاخص‌دار و برخی از دستورات صوتی پشتیبانی می‌کنند. واسط، تصریح‌کننده نوع عملیاتی است که رادیو اجازه انجام آن را به کاربر می‌دهد، اما نشان‌دهنده نحوه پیاده‌سازی عملیات در داخل رادیو نمی‌باشد.

به همین ترتیب، واسط یک کلاس توصیف‌کننده سرویس‌های (خدماتی) است که کلاس در اختیار سرویس‌گیرندگان خود قرار می‌دهد و نحوه تقاضای این سرویس‌ها را مشخص می‌سازد، اما چیزی از نحوه انجام کار به سرویس‌ها ارائه نمی‌کند. واسط یک کلاس متشکل از توابع عضو public که بعنوان سرویس‌های سراسری یا public کلاس هم شناخته می‌شوند، است. برای مثال، واسط کلاس GradeBook (شکل ۹-۳) حاوی یک سازنده و توابع عضو setCourseName، getCourseName و displayMessage است. سرویس‌گیرنده GradeBook (مثل main در شکل ۱۰-۳) از این توابع برای تقاضای سرویسی از کلاس استفاده می‌کند. همانطوری که بزودی مشاهده خواهید کرد، می‌توانید واسط یک کلاس را با نوشتن تعریف کلاسی که فقط لیستی از اسامی توابع عضو، نوع برگشتی و نوع پارامترها دارد، مشخص کنید.
تفکیک واسط از پیاده‌سازی

در مثال‌های قبلی، هر تعریف کلاس حاوی تعاریف کاملی از توابع عضو public کلاس و اعلان‌های از اعضای داده private آن بود. با این وجود، از لحاظ مهندسی نرم‌افزار بهتر خواهد بود تا توابع عضو در خارج از تعریف کلاس، تعریف گردند، بنابر این جزئیات پیاده‌سازی آنها از دید کد سرویس‌گیرنده‌ها پنهان خواهند ماند. با اینکار مطمئن خواهید شد که برنامه‌نویسان نخواهند توانست براساس جزئیات پیاده‌سازی کلاس شما، مبادرت به نوشتن کد سرویس‌گیرنده کنند. اگر چنین کاری کنند، در صورتی که ساختار پیاده‌سازی کلاس را تغییر دهید، به احتمال زیاد کد آنها با شکست مواجه خواهد شد.

برنامه موجود در شکل‌های ۱۱-۳ الی ۱۳-۳ مبادرت به جداسازی واسط کلاس GradeBook از بخش پیاده‌سازی آن با تقسیم تعریف کلاس شکل ۹-۳ به دو فایل، فایل سرآیند GradeBook.h (شکل ۱۱-۳) که در آن کلاس GradeBook تعریف شده و فایل کد منبع GradeBook.cpp (شکل ۱۲-۳) که توابع عضو GradeBook در آن تعریف شده‌اند، می‌کند. بطور قراردادی تعاریف توابع عضو در یک فایل کد منبع همنام با نام فایل سرآیند کلاس جای داده می‌شوند، بجز اینکه پسوند فایل .cpp است. فایل کد منبع fig03_13.cpp (شکل ۱۳-۳) تعریف‌کننده تابع main است (کد سرویس‌گیرنده). کد و خروجی شکل ۱۳-۳ یکسان با شکل۱۰-۳ است. شکل ۱۴-۳ نشاندهنده نحوه کامپایل این فایل برنامه از منظر برنامه‌نویس کلاس GradeBook و برنامه‌نویس کد سرویس‌گیرنده است، در ارتباط با این تصویر توضیحاتی ارائه خواهیم داد.
GradeBook.h: تعریف واسط کلاس با نمونه اولیه تابع

فایل سرآیند GradeBook.h (شکل ۱۱-۳) حاوی نسخه‌ دیگری از تعریف کلاس GradeBook است (خطوط ۹-۱۷). این نسخه شبیه به نسخه موجود در شکل ۹-۳ است، اما تعاریف تابع در شکل ۹-۳ با نمونه اولیه تابع (function prototype) جایگزین شده‌اند (خطوط ۱۲-۱۵)، که توصیف کننده واسط public کلاس است بدون اینکه پیاده‌سازی تابع عضو کلاس را آشکار کرده باشد. نمونه اولیه تابع، اعلانی از تابع است که به کامپایلر نام تابع، نوع برگشتی آن و نوع پارمترهای آن را بیان می‌کند. دقت کنید که فایل سرآیند هنوز هم مشخص‌کننده عضو داده private کلاس است (خط ۱۷). مجدداً بایستی کامپایلر از اعضای داده مطلع باشد تا بتواند میزان حافظه رزرو شده برای هر شی از کلاس را تعیین کند. با وارد کردن فایل سرآیند GradeBook.h در کد سرویس‌گیرنده (خط ۸ از شکل ۱۳-۳) این اطلاعات در اختیار کامپایلر قرار می‌گیرد.

نمونه اولیه تابع در خط ۱۲ از شکل ۱۱-۳ بر این نکته دلالت دارد که سازنده نیازمند یک پارامتر رشته‌‌ای است. بخاطر دارید که سازنده‌ها دارای نوع برگشتی نیستند، از اینرو نوع برگشتی در نمونه اولیه تابع قرار داده نشده است. نمونه اولیه تابع عضو setCourseName در خط ۱۳ نشان می‌دهد که setCourseName نیازمند یک پارامتر رشته‌ای بوده و مقداری برگشت نمی‌دهد (نوع برگشتی آن void است). نمونه اولیه تابع عضو getCourseName نشان می‌دهد که تابع نیازمند پارامتر نبوده و رشته برگشت می‌دهد (خط ۱۴).
cpp-ch3-11.jpg

شکل ۱۱-۳ | تعریف کلاس GradeBook حاوی نمونه اولیه تابع که مشخص‌کننده واسط کلاس است.

در پایان، نمونه اولیه تابع عضو displayMessage قرار دارد که مشخص می‌کند این تابع نیازمند پارامتر نبوده و مقداری هم برگشت نخواهد داد (خط ۱۵). این نمونه‌های اولیه تابع همانند سرآیندهای تابع متناظر در شکل ۹-۳ هستند، بجز اینکه اسامی پارامتر (که در نمونه‌های اولیه اختیاری هستند) در نظر گرفته نشده‌اند و اینکه نمونه اولیه هر تابع باید با یک سیمکولن خاتمه پذیرد.

GradeBook.cpp: تعریف توابع عضو در یک فایل کد منبع جداگانه

فایل کد منبع GradeBook.cpp شکل ۱۲-۳ تعریف‌کننده توابع عضو کلاس GradeBook است که در خطوط ۱۲-۱۵ از شکل ۱۱-۳ اعلان شده‌اند. تعریف تابع عضو در خطوط ۱۱-۳۴ قرار دارد و تقریباً با تعاریف موجود در خطوط ۱۵-۳۸ شکل ۹-۳ یکسان هستند.

توجه کنید که نام هر تابع عضو در سرآیندهای تابع (خطوط ۱۱، ۱۷، ۲۳ و ۲۹) پس از نام کلاس و :: قرار گرفته است، که بعنوان عملگر تفکیک قلمرو باینری شناخته می‌شود. این عملگر مبادرت به «گره زدن» هر تابع عضو با تعریف کلاس GradeBook (که هم اکنون جدا شده) می‌کند، که توابع عضو کلاس و اعضای داده را اعلان کرده است. در صورتیکه “GradeBook::” قبل از نام تابع قرار داده نشود، این توابع توسط کامپایلر بعنوان توابع عضو از کلاس GradeBook تشخیص داده نشده و کامپایلر آنها را همانند توابع «آزاد» یا «بی‌قاعده» همانند main در نظر می‌گیرد. چنین توابعی قادر به دسترسی به داده private کلاس GradeBook یا فراخوانی توابع عضو کلاس نخواهند بود. بنابر این، کامپایلر نمی‌تواند این توابع را کامپایل نماید. برای مثال، خطوط ۱۹ و ۲۵ که به متغیر courseName دسترسی پیدا می‌کنند می‌تواند سبب‌ساز خطای کامپایل شوند، چرا که courseName بعنوان یک متغیر محلی در هر تابع اعلان نشده است. کامپایلر اطلاعی ندارد که courseName بصورت یک عضو داده کلاس GradeBook اعلان شده است.
برای نشان دادن این که توابع عضو در GradeBook.cpp بخشی از کلاس GradeBook هستند، ابتدا فایل سرآیند GradeBook.h را وارد کرده‌ایم (خط ۸ از شکل ۱۲-۳). این کار به ما اجازه دسترسی به کلاسی بنام GradeBook در فایل GradeBook.cpp را می‌دهد. به هنگام کامپایل GradeBook.cpp، کامپایلر از اطلاعات موجود در GradeBook استفاده می‌کند تا مطمئن شود که

۱- اولین خط هر تابع عضو (خطوط ۱۱، ۱۷، ۲۳ و ۲۹) با نمونه اولیه خود در فایل GradeBook.h مطابقت دارد. برای مثال، کامپایلر مطمئن می‌شود که getCourseName پارامتری نمی‌پذیرد و یک رشته برگشت می‌دهد.
۲- هر تابع عضو، اعضای داده کلاس و سایر توابع را می‌شناسد. برای مثال، خط ۱۹ و ۲۵ می‌توانند به متغیر courseName دسترسی پیدا کنند، چرا که در GradeBook.h بعنوان یک عضو داده اعلان شده است، و خطوط ۱۳ و ۳۲ می‌تواند توابع setCourseName و getCourseName را به ترتیب فراخوانی نمایند، چرا که هر کدامیک از آنها بعنوان یک تابع عضو کلاس در GradeBook.h اعلان شده‌اند.

cpp-ch3-12.jpg


شکل ۱۲-۳ | تعاریف تابع عضو GradeBook نشاندهنده ساختار پیاده‌سازی کلاس GradeBook.
تست کلاس GradeBook

برنامه شکل ۱۳-۳ همان کار دستکاری شی GradeBook بکار رفته در برنامه ۱۰-۳ را انجام می‌دهد. جداسازی واسط GradeBook از بخش پیاده‌سازی توابع عضو تاثیری در روش استفاده این کد سرویس‌گیرنده از کلاس ندارد و فقط بر نحوه کامپایل برنامه و لینک آن تاثیر دارد که در مورد آن صحبت خواهیم کرد.
cpp-ch3-13.jpg

شکل ۱۳-۳ | کلاس GradeBook پس از جداسازی واسط از پیاده‌سازی.

همانند برنامه شکل ۱۰-۳، خط ۸ برنامه شکل ۱۳-۳ شامل فایل سرآیند GradeBook.h است و از اینرو است که کامپایلر می‌تواند از ایجاد و دستکاری صحیح شی‌های GradeBook در کد سرویس‌گیرنده مطمئن گردد. قبل از اجرای این برنامه، باید فایل‌های کد منبع در شکل ۱۲-۳ و ۱۳-۳ هر دو کامپایل‌شده و سپس به هم لینک گردند. فراخوانی تابع عضو در کد سرویس‌گیرنده نیازمند گره خوردن با پیاده‌سازی توابع عضو کلاس دارد، کاری که لینکر آن را انجام می‌دهد.
 
فرآیند کامپایل‌ و لینک

دیاگرام شکل ۱۴-۳ نمایشی از فرآیند کامپایل و لینک است که نتیجه آن یک برنامه اجرایی GradeBook بوده که می‌تواند توسط استاد بکار گرفته شود. غالباً واسط کلاس و ساختار پیاده‌سازی توسط یک برنامه‌نویس ایجاد و کامپایل می‌شود و توسط برنامه‌نویس دیگری که کد سرویس‌گیرنده کلاس را پیاده‌سازی کرده است، بکار گرفته می‌شود. بنابر این دیاگرام نشان‌دهنده نیازهای هر دو طرف برانه‌نویس کلاس و برنامه‌نویس کد سرویس‌گیرنده است. خطوط خط‌چین در دیاگرام نشاندهنده قسمت‌های مورد نیاز برنامه‌نویس کلاس، برنامه‌نویس کد سرویس‌گیرنده و کاربر برنامه GradeBook هستند. [نکته: شکل ۱۴-۳ یک دیاگرام UML نیست.



برنامه‌نویس کلاس مسئول ایجاد یک کلاس GradeBook با قابلیت استفاده مجدد در ایجاد فایل سرآیند GradeBook.h و فایل کد منبع GradeBook.cpp است که فایل سرآیند را وارد برنامه کرده (#include)، سپس فایل کد منبع را برای ایجاد کد شی GradeBook کامپایل می‌کند. برای پنهان ساختن جزئیات پیاده‌سازی توابع عضو GradeBook، برنامه‌نویس کلاس مبادرت به تدارک دیدن فایل سرآیند GradeBook.h برای برنامه‌نویس کد سرویس‌گیرنده و کد شی برای کلاس GradeBook می‌کند که حاوی دستورالعمل‌های زبان ماشین است که نشاندهنده توابع عضو می‌باشد. برنامه‌نویس کد سرویس‌گیرنده، فایل کد منبع را بطور آشکار بدست نمی‌آورد، از اینرو سرویس‌گیرنده از نحوه پیاده‌سازی توابع عضو GradeBook بی‌اطلاع باقی خواهد ماند.

cpp-ch3-14.png

شکل ۱۴-۳ | فرآیند کامپایل و لینک که یک برنامه اجرایی تولید می‌کند.

کد سرویس‌گیرنده فقط نیاز به شناخت واسط GradeBook به منظور نحوه استفاده از کلاس داشته و بایستی قادر به لینک آن به کد شی خود باشد. از آنجا که واسط کلاس بخشی از تعریف کلاس در فایل سرآیند GradeBook.h است، باید برنامه‌نویس کد سرویس‌گیرنده به این فایل دسترسی داشته و آن را در فایل کد منبع سرویس‌گیرنده وارد سازد (#include). زمانیکه کد سرویس‌گیرنده کامپایل می‌شود، کامپایلر از تعریف کلاس در GradeBook.h برای اطمینان از اینکه تابع main مبادرت به ایجاد و دستکاری صحیح شی‌های کلاس GradeBook می‌کند، استفاده می‌نماید. برای ایجاد برنامه اجرایی GradeBook قابل استفاده برای استاد (مربی)، آخرین مرحله لینک بصورت زیر است
۱- کد شی برای تابع main (یعنی، کد سرویس‌گیرنده)
۲- کد شی برای کلاس پیاده‌سازی‌کننده تابع عضو کلاس GradeBook
3- کد شی کتابخانه استاندارد C++ برای کلاس‌های ‍‍C++ (همانند string) بکار رفته توسط برنامه‌نویس پیاده‌سازی‌کننده کلاس و برنامه‌نویس کد سرویس‌گیرنده.

خروجی لینکر، برنامه اجرایی GradeBook است که استاد می‌تواند با استفاده از آن نمرات دانشجویان را مدیریت نماید. برای کسب اطلاعات بیشتر در ارتباط با کامپایل برنامه‌های با چند فایل منبع، به مستندات کامپایلر خود مراجعه کنید.
۱۰-۳ اعتبارسنجی داده با توابع set
در بخش ۶-۳ به معرفی توابع set پرداختیم که به سرویس‌گیرنده‌های کلاس اجازه تغییر در مقدار یک عضو داده private را می‌دادند. در برنامه شکل ۵-۳، کلاس GradeBook مبادرت به تعریف تابع عضو setCourseName کرده که فقط مقدار دریافتی از پارامتر name خود را به عضو داده courseName تخصیص می‌دهد. این عضو داده مطمئن نیست که نام دورة دریافتی مطابق با یک فرمت مشخص یا معتبر است.

همانطوری‌که در ابتدا بحث مطرح کردیم، فرض می‌کنیم که دانشگاه می‌تواند از برگه ثبت‌نام دانشجو که در آن اسامی دوره فقط ۲۵ کاراکتر یا کمتر طول دارند، چاپ بگیرد. اگر دانشگاه از سیستمی استفاده نماید که حاوی شی‌های GradeBook برای تولید رونوشت ثبت‌نامی است، باید کاری کنیم که کلاس GradeBook مطمئن شود که عضو داده courseName هرگز بیش از ۲۵ کاراکتر نخواهد داشت. برنامه شکل‌های ۱۵-۳ الی ۱۷-۳ سبب افزایش قابلیت تابع عضو setCourseName برای انجام فرآیند اعتبارسنجی می‌شوند.
تعریف کلاس GradeBook

توجه کنید که تعریف کلاس GradeBook در شکل ۱۵-۳ و واسط آن، یکسان با شکل ۱۱-۳ است. از آنجا که واسط بدون تغییر باقی مانده، سرویس‌گیرنده‌های این کلاس به هنگام تغییر در تعریف تابع setCourseName، نیازی به اصلاح نخواهند داشت. این ویژگی سبب می‌شود که سرویس‌گیرنده‌ها از مزیت کلاس ارتقاء یافته GradeBook به آسانی و با لینک کد سرویس‌گیرنده به کد شی GradeBook ارتقاء یافته، برخوردار شوند.
اعتبارسنجی نام دوره با تابع عضو setCourseName
ارتقاء و بهبود کلاس GradeBook در تعریف تابع عضو setCourseName صورت می‌گیرد (شکل ۱۶-۳، خطوط ۱۸-۳۱). عبارت if در خطوط ۲۰-۲۱ تعیین می‌کند که آیا پارامتر name حاوی نام یک دوره معتبر (رشته‌ای به طول ۲۵ کاراکتر یا کمتر) است یا خیر.
اگر نام دوره معتبر باشد، خط ۲۱ مبادرت به ذخیره نام دوره در عضو داده courseName می‌کند. به عبارت name.length() در خط ۲۰ توجه کنید. این عبارت فراخوانی یک تابع عضو همانند myGradeBook.displayMessage() است. کلاس string متعلق به کتابخانه استاندارد C++ دارای تابع عضوی بنام length است که تعداد کاراکترهای موجود در یک شیstring را برگشت می‌دهد. پارامتر name یک شی از نوع رشته (string) است و از اینرو فراخوانی name.length() تعداد کاراکترهای موجود در name را برگشت می‌دهد. اگر این مقدار کمتر یا برابر ۲۵ باشد، نام دریافتی معتبر بوده و خط ۲۱ اجرا می‌شود.
cpp-ch3-15.jpg

شکل ۱۵-۳ | تعریف کلاس GradeBook.
cpp-ch3-161.jpg


cpp-ch3-162.jpg


شکل ۱۶-۳ | تعریف تابع عضو برای کلاس GradeBook با تابع set که مبادرت به اعتبارسنجی طول عضو داده courseName می‌کند.

عبارت if در خطوط ۲۳-۳۰ به حالتی رسیدگی می‌کند که setCourseName نام یک دورة نامعتبر دریافت کرده است (نامی که بیش از ۲۵ کاراکتر طول دارد). حتی اگر پارامتر name بسیار طولانی باشد، می‌خواهیم که شی GradeBook را در یک وضعیت پایدار حفظ کنیم. وضعیتی که در آن عضو داده courseName حاوی یک مقدار معتبر باشد (رشته‌ای بطول ۲۵ کاراکتر یا کمتر). از اینرو، مبادرت به کوتاه کردن نام دوره و تخصیص ۲۵ کاراکتر اول name به عضو داده courseName می‌کنیم (البته این روش کوتاه‌سازی نام دوره چندان جالب نیست). کلاس استاندارد string دارای تابع عضوی بنام substrsubstring“) است که یک شی جدید string با کپی کردن بخشی از شی string موجود، تهیه می‌کند. با فراخوانی خط ۲۶، عبارت name.substr(0,25) دو عدد صحیح (۰, ۲۵) به تابع عضو substr شی name ارسال می‌شوند. این آرگومان‌ها نشاندهنده بخشی از رشته name هستند که substr آن را برگشت خواهد داد. آرگومان اول نشان‌دهنده موقعیت شروع در رشته اصلی است که کاراکترها از آن موقعیت شروع به کپی شدن خواهند کرد، توجه کنید که موقعیت اولین کاراکتر در هر رشته‌ای با صفر شروع می‌شود. آرگومان دوم نشان‌دهنده تعداد کاراکترهایی است که باید کپی شوند. بنابر این با فراخوانی خط ۲۶، بیست و پنج کاراکتر از رشته name از موقعیت صفر برگشت داده خواهد شد. برای مثال اگر نام موجود نگهداری شده “CS101 Introduction to Programming in C++” باشد، تابع substr رشته “CS101 Introduction to Pro” را برگشت خواهد داد. پس از فراخوانی substr، خط ۲۶ زیر رشته برگشتی توسط substr را به عضو داده courseName تخصیص می‌دهد. در این حالت، تابع عضو setCourseName مطمئن خواهد بود که courseName همیشه یک رشته بطول ۲۵ کاراکتر یا کمتر خواهد داشت. اگر تابع عضو مجبور به کوتاه کردن نام دوره برای تبدیل آن به یک مقدار معتبر شود، خطوط ۲۸-۲۹ یک پیغام هشدار به نمایش در می‌آورند. (کوتاه شده جمله ”

توجه کنید عبارت if در خطوط ۲۳-۳۰ حاوی دو عبارت در بدنه خود است، یکی برای تنظیم courseName با ۲۵ کاراکتر اول از پارامتر name و یکی برای چاپ پیغام اطلاع‌ دهنده به کاربر. مایل بودیم تا هر دو این عبارات در زمانیکه طول name طولانی‌تر از حد مجاز باشند اجرا گردند، بنابر این هر دو آنها را در درون یک جفت براکت، {} قرار داده‌ایم. از فصل دوم بخاطر دارید که این براکت‌ها بلوک ایجاد می‌کنند. در فصل چهارم اطلاعات بیشتری در زمینه قرار دادن عبارات مضاعف در بدنه یک عبارت کنترلی بدست خواهید آورد.
دقت کنید که عبارت cout در خطوط ۲۸-۲۹ بدون عملگر درج در ابتدای خط دوم ظاهر شده است:
cout<<”Name\”"<<name<<”\”exceeds maximum length(25).\n”
“Limiting courseName to first 25 characters.\n”<<endl;

کامپایلر C++ مبادرت به ترکیب رشته‌های لیترال مجاور هم می‌کند، حتی اگر این رشته‌ها بر روی خطوط مجزا شده از هم در برنامه قرار داشته باشند. بنابر این در عبارت فوق، کامپایلر C++ شروع به ترکیب رشته‌های لیترال “\”exceeds maximum length(25).\n” و “Limiting courseName to first 25 characters.\n” در یک رشته لیترال واحد در خروجی همانند خطوط ۲۸-۲۹ برنامه شکل ۱۶-۳ می‌کند. چنین رفتاری امکان می‌دهد تا رشته‌های طولانی را در چندین خط قرار دهید بدون اینکه مجبور به استفاده از چندین عملگر درج باشید.
cpp-ch3-17.jpg

شکل ۱۷-۳ | ایجاد و دستکاری شی GradeBook که در آن نام دوره محدود به ۲۵ کاراکتر است.
تست کلاس GradeBook

برنامه شکل ۱۷-۳ به توصیف نسخه اصلاح شده کلاس GradeBook (برنامه‌های ۱۵-۳ و ۱۶-۳) در زمینه اعتبارسنجی است. در خط ۱۴ یک شی GradeBook بنام gradeBook1 ایجاد شده است. بخاطر دارید که سازنده GradeBook تابع عضو setCourseName را برای مقداردهی اولیه عضو داده courseName فراخوانی می‌کرد. در نسخه‌های قبلی این کلاس، مزیت فراخوانی setCourseNamesetCourseName برخوردار می‌شود. سازنده بجای اینکه کد اعتبارسنجی خود را تکثیر یا تکرار کند، فقط مبادرت به فراخوانی ساده setCourseName می‌کند. زمانیکه خط ۱۴ از برنامه شکل ۷-۳ نام اولیه دورة “CS101 Introduction to Programming in C++” را به سازنده GradeBook ارسال می‌کند، سازنده این مقدار را به setCourseName انتقال می‌دهد، جائیکه مقداردهی اولیه واقعی در آنجا اتفاق می‌افتد. به دلیل اینکه نام دورة حاوی بیش از ۲۵ کاراکتر است، بدنه دومین عبارت if اجرا می‌شود، و در نتیجه courseName با ۲۵ کاراکتر اول نام دوره یعنی”CS101 Introduction to Pro” مقداردهی می‌گردد. توجه کنید که خروجی در شکل ۱۷-۳ حاوی پیغام هشدار ایجاد شده توسط خطوط ۲۸-۲۹ از شکل ۱۶-۳ در تابع عضو setCourseName است. خط ۱۵ یک شی دیگر از GradeBook بنام gradeBook2 ایجاد می‌کند. نام یک دوره معتبر که دقیقا برابر ۲۵ کاراکتر است به سازنده ارسال شده است. توسط سازنده مورد بررسی قرار نگرفت، که در اینجا به بررسی آن می‌پردازیم. سازنده از مزیت اعتبارسنجی تدارک دیده شده توسط
خطوط ۱۸-۲۱ از شکل ۱۷-۳ نام دوره کوتاه شده برای gradeBook1 و نام دوره برای gradeBook2 را به نمایش در می‌آورند. خط ۲۴ مستقیماً تابع عضو setCourseName شی gradeBook1 را فراخوانی می‌کند تا نام دوره در شی GradeBook را به یک نام کوتاهتر تغییر دهد تا دیگر نیازی به کوتاه‌سازی آن نباشد سپس خطوط ۲۷-۳۰ اسامی دوره را مجدداً به نمایش در می‌آورند.
 
نکاتی دیگر در ارتباط توابع set

یک تابع public set همانند setCourseName بایستی بدقت مراقب هرگونه تغییر در مقدار یک عضو داده (همانند courseName) باشد تا مطمئن گردد که مقدار جدید مناسب این ایتم داده است. برای مثال، مبادرت به تنظیم روزی از ماه به ۳۷ نبایستی قبول شود، مباردت به تنظیم وزن یک شخص با صفر یا مقدار منفی برای آن نبایستی پذیرفته شود یا در صورتیکه حداکثر امتیاز یا نمره شخصی می‌تواند بین صفر تا ۱۰۰ باشد، نبایستی امتیاز ۱۸۵ تایید شود.



توابع set یک کلاس می‌توانند مقادیری به سرویس‌گیرنده‌های کلاس برگشت دهند تا نشان دهند که مبادرت به تخصیص یک داده نامعتبر به شی از کلاس شده است. سرویس‌گیرنده کلاس می‌تواند مبادرت به تست مقدار برگشتی از یک تابع set کند تا تعیین نماید که آیا عملیات اصلاح یا تغییر در شی موفقیت‌آمیز بوده است یا خیر و در هر دو حالت کار مقتضی را انجام دهد. در فصل ۱۶ به بررسی نحوه اطلاع‌دهی؛ سرویس‌گیرنده‌های کلاس از طریق مکانیزم رسیدگی به استثناء خواهیم پرداخت. برای اینکه برنامه ۱۵-۳ الی ۱۷-۳ را فعلاً در یک سطح ساده نگهداری کنیم، تابع setCourseName در برنامه ۱۶-۳ فقط مبادرت به چاپ پیغام‌ مقتضی در صفحه نمایش می‌کند.
۱۱-۳ مبحث آموزشی مهندسی نرم‌افزار: شناسایی کلاس‌های موجود در مستند نیازهای ATM

در این بخش شروع به طراحی سیستم ATM می‌کنیم که در فصل دوم به معرفی آن پرداختیم. در این بخش به شناسایی کلاس‌ها می‌پردازیم که در ایجاد سیستم ATM مورد نیاز هستند و این کار را با تحلیل اسامی و اصطلاحات آمده در مستند نیازهای ATM می‌کنیم. همچنین به معرفی دیاگرام‌های کلاس UML به منظور مدل کردن روابط مابین این کلاس‌ها خواهیم پرداخت. این کار بعنوان اولین گام در تعریف ساختار سیستم از اهمیت خاصی برخوردار است.

شناسایی کلاس‌ها در سیستم

فرآیند OOD خود را با شناسایی کلاس‌های مورد نیاز در ایجاد یک سیستم ATM آغاز می‌کنیم. سرانجام این کلاس‌ها را با استفاده از دیاگرام‌های کلاس UML و پیاده‌سازی این کلاس‌ها در C++ توصیف خواهیم کرد. ابتدا، نگاهی به مستند نیازها در بخش ۸-۲ می‌اندازیم تا اسامی و اصطلاحات کلیدی را که می‌توانند به ما در شناسایی کلاس‌های که در ایجاد سیستم ATM نقش دارند، پیدا کنیم. امکان دارد تصمیم بگیریم که برخی از این اسامی و اصطلاحات جزو صفات کلاس‌های دیگر در سیستم باشند. همچنین می‌توانیم استنتاج ‌کنیم که برخی از اسامی ارتباطی با بخش‌های سیستم ندارند و نیازی نیست که آنها را مدل‌سازی نمائیم. کلاس‌های اضافی می‌توانند میزان حرکت ما را در فرآیند طراحی آشکار کنند.

جدول شکل ۱۸-۳ لیستی از اسامی و اصطلاحات موجود در مستند‌ نیازها را به نمایش در آورده است. در این لیست ترتیب از سمت چپ به راست و با توجه به ظاهر شدن این اسامی در مستند نیازها است.

اسامی و اصطلاحات موجود در مستند نیازها بانک پول/ سرمایه شماره حساب ATM صفحه نمایش PIN کاربر صفحه کلید پایگاه داده بانک مشتری تحویل‌دار خودکار درخواست موجودی تراکنش اسکناس ۲۰ دلاری/ نقد برداشت پول حساب شکاف سپرده‌گذاری سپرده موجودی پاکت سپرده
شکل ۱۸-۳ | اسامی و اصطلاحات موجود در مستند نیازها.

کلاس‌ها را فقط برای اسامی و اصطلاحاتی که در سیستم ATM از اهمیت برخوردار هستند ایجاد می‌کنیم. نیازی به مدل کردن بانک بعنوان یک کلاس نداریم، چرا که بانک بخشی از سیستم ATM نیست، بانک فقط از ما خواسته که سیستم ATM را ایجاد کنیم. مشتری و کاربر نیز نشاندهنده موجودیت‌های خارج از سیستم هستند، این موجودیت‌ها از اهمیت برخوردار می‌باشند چرا که آنها در تعامل با سیستم قرار می‌گیرند، اما نیازی به مدل کردن آنها به عنوان کلاس‌ها در نرم‌افزار ATM نداریم. بخاطر دارید که کاربر ATM (مشتری بانک) را بصورت یک بازیگر در دیاگرام حالت شکل ۱۸-۲ به نمایش در آوردیم.

نیازی به مدل کردن «اسکناس ۲۰ دلاری» یا «پاکت سپرده» بعنوان کلاس نیست. این موارد شی‌های فیزیکی در دنیای واقعی هستند، اما بخشی از فرآیند اتوماتیک‌سازی نمی‌باشند. می‌توانیم بقدر کفایت اسکناس را در سیستم با استفاده از صفات کلاسی که پرداخت‌کننده یا تحویل‌دار اتوماتیک را مدل می‌کند، عرضه کنیم. در بخش ۱۳-۴ به تخصیص صفات به کلاس‌ها خواهیم پرداخت. برای مثال، تحویل‌دار اتوماتیک مبادرت به نگهداری تعدادی اسکناس در خود می‌کند. مستند نیازها چیزی در ارتباط با کاری که سیستم باید با پاکت سپرده‌ها پس از دریافت آنها انجام دهد، بیان نمی‌کند. می‌توانیم فرض کنیم که فقط تایید وصول پاکت صورت می‌گیرد، عملیاتی که توسط کلاس مدل شده برای شکاف سپرده انجام می‌شود. در بخش ۲۲-۶ با نحوه تخصیص عملیات به کلاس‌ها آشنا خواهید شد.

در این سیستم ATM ساده شده، نمایش مقادیر مختلف “پولی” شامل “موجودی” یک حساب، همانند صفات سایر کلاس‌ها ضروری بنظر می‌رسد. همچنین، اسامی “شماره‌حساب” و “PIN” نشاندهنده اطلاعات با ارزش در سیستم ATM هستند. این موارد از صفات مهم در یک حساب بانکی هستند. با این وجود، نشاندهنده رفتاری نمی‌باشند. از اینرو بهتر خواهد بود تا آنها را بعنوان صفات کلاس حساب مدل‌سازی کنیم.

با وجود آنکه در مستند نیازها کلمه “تراکنش” بصورت کلی بکار گرفته شده است، اما فعلاً قصد مدل کردن این مفهم کلی در تراکنش مالی را نداریم. بجای آن سه نوع تراکنش (“نمایش موجودی”، “برداشت پول” و “سپرده”) را بعنوان کلاس‌های مجزا مدل می‌کنیم. این کلاس‌ها دارای صفات خاص مورد نیاز برای انجام تراکنش‌های متعلق بخود را دارا هستند. برای مثال، کلاس برداشت پول نیاز به داشتن میزان پولی دارد که کاربر می‌خواهد برداشت کند. با این وجود، کلاس موجودی، نیازی به داده‌های اضافی ندارد. علاوه بر اینها، سه کلاس تراکنشی رفتارهای منحصر به فردی را در معرض دید قرار می‌دهند. کلاس برداشت پول شامل پرداخت پول به کاربر است، در حالیکه سپرده‌گذاری مستلزم دریافت پاکت سپرده‌گذاری از سوی کاربر است. [نکته: در بخش ۱۰-۱۳، مبادرت به فاکتورگیری ویژگی‌های مشترک از تمام تراکنش‌ها بصورت یک کلاس «تراکنش» کلی خواهیم کرد. با استفاده از مفهوم کلاس‌های انتزاعی و توارث در برنامه‌نویسی شی‌گرا.]

کلاس‌های مورد نیاز سیستم را بر پایه اسامی و اصطلاحات موجود در جدول ۱۸-۳ تعیین می‌کنیم. هر کدامیک از این اسامی به یک یا چند مورد از عبارات زیر مراجعه دارند:

  • ATM
  • صفحه نمایش
  • صفحه کلید
  • تحویل‌دار اتوماتیک
  • شکاف سپرده‌گذاری
  • حساب
  • پایگاه داده بانک
  • نمایش موجودی (درخواست موجودی)
  • برداشت پول
  • سپرده‌گذاری

عناصر موجود در این لیست از قابلیت تبدیل شدن به کلاس‌های مورد نیاز در پیاده‌سازی سیستم ATM برخوردار هستند. اکنون می‌توانیم شروع به مدلسازی کلاس‌ها در سیستم خود بر پایه لیست فوق کنیم. اسامی کلاس‌ها را در فرآیند طراحی با حروف بزرگ نشان می‌دهیم (قاعده UML)، همین کار را به هنگام پیاده‌سازی طراحی به زبان C++ انجام خواهیم داد. اگر نام کلاسی بیش از یک کلمه باشد، کلمات را در کنار هم قرار می‌دهیم و هر کلمه را با حرف بزرگ شروع می‌کنیم (مثلاً Multiple WordName). با استفاده از این روش، مبادرت به ایجاد کلاس‌های ATM، Screen (صفحه نمایش)، صفحه‌کلید (Keypad)، تحویل‌دار خودکار یا پرداخت‌کننده پول (CashDispencer)، شکاف‌ سپرده‌ (DepositSlot)، حساب (Account)، پایگاه داده بانک (BankDatabase)، پرس‌وجوی میزان موجودی (BalanceInquiry)، برداشت پول (Withdrawal) و سپره (Deposit). سیستم خود را با استفاده از تمام این کلاس‌ها بعنوان بلوک‌های سازنده ایجاد خواهیم کرد. قبل از شروع به ایجاد بلوک‌های سازنده سیستم، بایستی درک مناسبی از روابط مابین کلاس‌ها بدست آوریم.
مدل‌سازی کلاس‌ها

زبان UML امکان مدل‌سازی کلاس‌های موجود در سیستم ATM و روابط داخلی آنها را از طریق دیاگرام‌های کلاس فراهم آورده است. در شکل ۱۹-۳ کلاس ATM نشان داده شده است. در UML، هر کلاس بصورت یک مستطیل با سه بخش مدل می‌شود.

بخش فوقانی حاوی نام کلاس در وسط و بصورت توپر (پر رنگ) نوشته می‌شود. بخش میانی حاوی صفحات کلاس است (در بخش ۱۳-۴ و بخش ۱۱-۵ به بررسی صفات خواهیم پرداخت) بخش تحتانی حاوی عملیات کلاس است (در بخش ۲۲-۶ بحث خواهد شد). در شکل ۱۹-۳ بخش میانی و تحتانی خالی هستند، چرا که هنوز به تعیین صفات و عملیات کلاس نپرداخته‌ایم.

دیاگرام‌های کلاس از قابلیت نمایش روابط مابین کلاس‌های سیستم برخوردار هستند. شکل ۲۰-۳ نشاندهنده نحوه رابطه کلاس‌های ATM و Withdrawal با یکدیگر است. برای سادگی کار، در این لحظه فقط مبادرت به مدل‌سازی این زیر مجموعه می‌کنیم. دیاگرام کامل کلاس را در ادامه این بخش شاهد خواهید بود. دقت کنید که مستطیل‌های نشاندهنده کلاس‌ها در این دیاگرام به زیربخش‌ها تقسیم نمی‌شوند.

در شکل ۲۰-۳، خط مستقیم و یکپارچه دو کلاس را به هم پیوند زده، نشاندهنده یک وابستگی است (رابطه مابین کلاس‌ها). اعداد قرار گرفته در انتهای خط مقادیر تعدد هستند که نشان می‌دهند که چند شی از هر کلاس در وابستگی (رابطه) شرکت یا دخالت دارند.
cpp-ch3-19.png

شکل ۱۹-۳ | نمایش کلاس در UML با استفاده از دیاگرام کلاس.

cpp-ch3-20.png



شکل ۲۰-۳ | دیاگرام‌های کلاس در حال نمایش وابستگی مابین کلاس‌ها.

در این مورد، با دنبال کردن خط از یک طرف به طرف دیگر معلوم می‌شود که در هر لحظه، یک شی ATM در رابطه (وابستگی) با صفر یا یک شی Withdrawal شرکت دارد. اگر کاربر جاری در حال حاضر تراکنشی انجام ندهد یا تقاضای یک تراکنش از نوع دیگری را نماید، صفر، و در صورتیکه تقاضای برداشت پول (withdrawal) کند، مقدار ۱ بکار گرفته می‌شود. زبان UML قادر به مدل کردن انواع تعدد یا کثرت است. در جدول شکل ۲۱-۳ لیستی از انواع تعددها و مفهوم آنها آورده شده است.

نماد مفهوم ۰ هیچ- اصلاً ۱ یک m مقدار صحیح ۰٫٫۱ صفر یا یک m,n m یا n m..n حداقل m، اما نه بیشتر از n * هر مقدار غیرمنفی (صفر یا بیشتر) ۰٫٫* صفر یا بیشتر (همانند *) ۱٫٫* یک یا بیشتر
شکل ۲۱-۳ | انواع تعدد.

وابستگی می‌تواند نام داشته باشد. برای مثال، کلمه Executes در بالای خط متصل‌کننده کلاس‌های ATM و Withdrawal در شکل ۲۰-۳ نشان‌دهنده نام این وابستگی (ارتباط) است. این بخش از دیاگرام بصورت زیر معنی می‌شود، “یک شی از کلاس ATM صفر یا یک شی از کلاس Withdrawal را به اجرا در می‌آورد.” توجه نمایید که اسامی وابستگی، حالت هدایت‌کننده و نشان‌دهنده جهت هستند، همانند جهت فلش توپر، از اینرو جهت تفسیر دیاگرام مهم است، در صورتی که برای مثال تفسیر را از سمت راست به چپ انجام دهیم، جمله‌ای به این مضمون خواهیم داشت “صفر یا یک شی از کلاس Withdrawal یک شی از کلاس ATM را به اجرا درمی‌آورد.”

کلمه currentTransaction در کنار و زیرخط وابستگی Withdrawal در شکل ۲۰-۳، نام یک نقش (role) است، که هویت‌دهنده نقشی است که شی Withdrawal در رابطه خود با ATM بازی می‌کند. نام نقش، هدف و منظوری را به رابطه مابین‌ کلاس‌‌ها اضافه می‌کند، و اینکار را با شناسایی نقشی که کلاس در بافت رابطه ایفاء می‌کند، انجام می‌دهد. یک کلاس می‌تواند چندین نقش در همان سیستم بازی کند. برای مثال، در یک سیستم پرسنلی دانشگاه یک نفر می‌تواند نقش یک «پورفسور» را به هنگام رابطه داشتن با دانشجویان بازی کند. امکان دارد همان شخص نقش «همکار» در کنار پورفسور دیگر و نقش «مربی» را به هنگام مسابقات دانشجویی بازی کند. در شکل ۲۰-۳، نام نقش currentTransaction بر این نکته دلالت دارد که شی Withdrawal در اجرای (Executes) رابطه با یک شی از کلاس ATM دخالت دارد و این کلاس در پردزاش تراکنش جاری عمل می‌کند. در سایر محیط‌ها امکان دارد یک شی Withdrawal نقش‌های متفاوتی بخود گیرد. زمانیکه مفهوم رابطه در دیاگرام به قدر کافی گویا باشد، از اسامی نقش در دیاگرام‌های کلاس استفاده نمی‌شود.

علاوه بر وجود روابط ساده، وابستگی می‌تواند تصریح‌کننده روابط بسیار پیچیده و مرکب باشد، همانند زمانیکه شی‌های از یک کلاس با شی‌های از کلاس‌های دیگر ترکیب شوند. به سیستم ATM واقعی توجه کنید. سازنده ATM باید چه قسمت‌های را در کنار هم قرار دهد تا ATM قادر به کار باشد؟ مستند نیازها به ما می‌گوید که ATM دستگاهی متشکل از یک صفحه نمایش، یک صفحه‌کلید، یک پرداخت‌کننده یا تحویل‌دار خودکار و یک شکاف سپرده‌گذاری است.

در شکل ۲۲-۳ لوزی‌های توپر متصل شده به خطوط وابستگی کلاس ATM بر این نکته دلالت دارند که کلاس ATM دارای یک رابطه ترکیبی با کلاس‌های Screen، Keypad، CashDispenser و DepositSlot است. ترکیب مفهومی از یک رابطه کامل/بخش (قطعه) است. کلاسی که دارای نماد ترکیب (لوزی توپر) در انتها خط وابستگی خود می‌باشد، کامل بوده (در این مورد ATM) و کلاس‌های قرار گرفته در آن سوی خطوط وابستگی، بخش یا قطعه می‌باشند، در این مورد کلاس‌های Screen، Keypad، CashDispenser و DepositSlot. ترکیب بنمایش درآمده در شکل ۲۲-۳ نشان می‌دهد که یک شی از کلاس ATM از یک شی از کلاس Screen، یک شی از کلاس CashDispenser، یک شی از کلاس Keypad و یک شی از کلاس DepositSlot تشکیل یافته است. ATM «دارای» یک صفحه نمایش، یک صفحه‌کلید، یک تحویل‌دار خودکار و شکاف سپرده است. رابطه «داشتن» تعریف‌کننده ترکیب است. در فصل سیزدهم نشان خواهیم داد که رابطه «است یک» تعریف‌کننده توارث است.
بر طبق مشخصات UML، رابطه ترکیب دارای خصوصیات زیر است:

۱- فقط یک کلاس در رابطه می‌تواند نشاندهنده رابطه کامل باشد (لوزی می‌تواند فقط در انتهای یک طرف خط وابستگی قرار گرفته باشد). برای مثال خواه صفحه نمایش بخشی از ATM باشد یا ATM بخشی از صفحه نمایش باشد، صفحه نمایش و ATM هر دو نمی‌توانند نشاندهنده رابطه کامل (سراسری) باشند.

۲- قطعات یا بخش‌ها در رابطه ترکیب تا زمانیکه رابطه کامل وجود دارد، وجود خواهند داشت و این رابطه کامل مسئول ایجاد و نابود کردن قطعات متعلق بخود است. برای مثال، اقدام به ایجاد یک ATM مستلزم ساخت قطعات آن است. علاوه بر این، اگر ATM نابود گردد، صفحه نمایش، صفحه‌کلید، پرداخت‌کننده اتوماتیک و شکاف سپرده آن نیز نابود خواهند شد.
۳- یک قطعه می‌تواند فقط در یک زمان متعلق به یک رابطه کامل باشد، اگر چه امکان دارد قطعه‌ای حذف و به رابطه کامل دیگری متصل گردد.

لوزی‌های بکار رفته در دیاگرام کلاس ما بر این نکته دلالت دارند که در رابطه ترکیبی این سه خصیصه وجود دارند. اگر رابطه «داشتن» قادر به برآوردن یکی از چند ضابطه فوق نباشد، UML بر استفاده از لوزی‌های توخالی متصل شده به انتهای خطوط وابستگی تصریح می‌کند تا نشاندهنده اجتماع یا تراکم باشند. اجتماع نسخه ضعیف‌تر ترکیب است. برای مثال، یک کامپیوتر و مانیتور در رابطه اجتماع قرار دارند، کامپیوتر «دارای» مانیتور است، اما دو قطعه می‌توانند بصورت مستقل وجود داشته باشند و همان مانیتور می‌تواند در یک زمان به چندین کامپیوتر متصل گردد، از اینرو اینحالت نقض خصیصه دوم و سوم ترکیب است.
cpp-ch3-22.png

شکل ۲۲-۳ | دیاگرام کلاس نشاندهنده رابطه ترکیب.

در شکل ۲۳-۳ نمایشی از دیاگرام کلاس سیستم ATM ارائه شده است. این دیاگرام اکثر کلاس‌هایی که در ابتدای این بخش شناسایی کرده بودیم را به همراه وابستگی مابین آنها را که از مستند نیازها استتناج کرده‌ایم، مدل کرده است. [نکته: کلاس‌های BalanceInquiry و Deposit در رابطه مشابهی با کلاس Withdrawal شرکت دارند، از اینرو برای حفظ سادگی دیاگرام، آنها را در نظر نگرفته‌ایم. در فصل ۱۳، دیاگرام کلاس را گسترش داده و تمام کلاس‌های موجود در سیستم ATM را وارد آن می‌کنیم.]

شکل ۲۳-۳ نمایشی از مدل گرافیکی ساختار سیستم ATM است. این دیاگرام کلاس حاوی کلاس‌های BankDatabase و Account و چندین رابطه (وابستگی) است که در شکل‌های ۲۰-۳ یا ۲۲-۳ عرضه نشده بودند. دیاگرام کلاس نشان می‌دهد که کلاس ATM دارای یک رابطه یک به یک با کلاس BankDatabase است، یک شی ATM مبادرت به تصدیق کاربران در برابر یک شی BankDatabaseBankDatabase در یک رابطه ترکیبی با صفر یا چندین شی از کلاس Account شرکت دارد. از جدول ۲۱-۳ بخاطر دارید که مقدار تعدد یا کثرت ۰٫٫* در سمت وابستگی Account مابین کلاس BankDatabase و کلاس Account بر این نکته دلالت دارد که صفر یا چندین شی از کلاس Account بخشی در رابطه را تشکیل می‌دهند. کلاس BankDatabase دارای رابطه یک به چند با کلاس Account است. BankDatabase مبادرت به ذخیره‌ حساب‌های (accounts) متعدد در خود می‌کند. به همین ترتیب، کلاس Account دارای رابطه چند به یک با کلاس BankDatabase است، چرا که حساب‌های متعدد در پایگاه داده بانک (bank database) ذخیره می‌شوند. [نکته: از جدول شکل ۲۱-۳ بخاطر دارید که مقدار * معادل با ۰٫٫* است. برای افزایش وضوح دیاگرام‌های کلاس از نماد ۰٫٫* استفاده کرده‌ایم.] می‌کند. همچنین در شکل ۲۳-۳، مبادرت به مدل کردن این واقعیت کرده‌ایم که پایگاه بانک حاوی اطلاعاتی در ارتباط با حساب‌های متعدد است، یک شی از کلاس
cpp-ch3-23.png

شکل ۲۳-۳ | دیاگرام کلاس برای مدل کردن سیستم ATM.

همچنین شکل ۲۳-۳ نشان می‌دهد که اگر کاربر مبادرت به برداشت پول کند، «یک شی از کلاس Withdrawal به موجودی حساب دسترسی پیدا کرده/آنرا از طریق یک شی از کلاس BankDatabaseWithdrawal و کلاس Account ایجاد کنیم. با این وجود، مستند نیازها شرح می‌دهد که «ATM باید با پایگاه داده اطلاعات حساب بانک در تعامل قرار داشته باشد» تا تراکنش‌ها قابل انجام باشند. حساب بانکی حاوی اطلاعات حساس بوده و مهندسان سیستم بایستی همیشه مراقب امنیت داده افراد به هنگام طراحی سیستم باشند. از اینرو، فقط BankDatabase می‌تواند مبادرت به دسترسی و اعمال تغییر مستقیم در یک حساب کند. تمام قسمت‌های دیگر سیستم باید با پایگاه داده در تعامل قرار گیرند تا بتوانند اطلاعاتی بدست آورده یا حساب را به روز نمایند. تغییر می‌دهد.» می‌توانیم یک رابطه مستقیم مابین کلاس

همچنین دیاگرام کلاس در شکل ۲۳-۳ مبادرت به مدل کردن رابطه موجود مابین کلاس Withdrawal و کلاس‌های Screen، CashDispenser و Keypad می‌کند. تراکنش برداشت پول شامل اعلان پیغامی به کاربر برای تعیین میزان پول برداشتی و دریافت ورودی عددی است. این اعمال به ترتیب مستلزم استفاده از صفحه نمایش و صفحه کلید است. علاوه بر اینها، پرداخت پول نقد به کاربر مستلزم دسترسی به تحویل‌دار خودکار (پرداخت‌کننده پول خودکار) می‌باشد.

کلاس‌های BalanceInquiry و Deposit که در شکل ۲۳-۳ آورده نشده‌اند، دارای چندین رابطه با کلاس‌های دیگر در سیستم ATM هستند. همانند کلاس Withdrawl، هر کدامیک از این کلاس‌ها با کلاس‌هایATM و BankDatabase دارای رابطه (وابستگی) هستند. یک شی از کلاس BalanceInquiry دارای رابطه‌ای با یک شی از کلاس Screen برای نمایش میزان موجودی در حساب یک کاربر نیز است. کلاس Deposit با کلاس‌های Screen، Keypad و DepositSlot در ارتباط است. همانند برداشت پول، تراکنش سپرده‌گذاری مستلزم استفاده از صفحه‌نمایش و صفحه‌کلید برای نمایش پیغامی به کاربر و دریافت ورودی است. برای دریافت پاکت سپرده، یک شی از کلاس Deposit به شکاف سپرده دسترسی پیدا می‌کند.

اکنون کلاس‌های موجود در سیستم ATM خود را شناسایی کرده‌ایم. در بخش ۱۳-۴ به تعیین صفات هر کدامیک از این کلاس‌ها خواهیم پرداخت. در بخش ۱۱-۵ از این صفات برای بررسی نحوه تغییر عملکرد سیستم در زمان استفاده می‌کنیم. در بخش ۲۲-۶ به تعیین عملیاتی که کلاس‌ها در سیستم انجام خواهند داد، می‌پردازیم.
 
مقدمه

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



در این فصل به معرفی عبارات if، if..else و while در زبان C++ خواهیم پرداخت. سه بلوک سازنده که به برنامه‌نویسان امکان می‌دهند تا منطق مورد نیاز توابع عضو را برای انجام وظایف مشخص کنند. بخشی از این فصل و فصل‌های ۵ و ۷ را برای توسعه دادن کلاس GradeBook معرفی شده در فصل سوم اختصاص داده‌ایم. در واقع، یک تابع عضو به کلاس GradeBook اضافه می‌کنیم که از عبارات کنترلی برای محاسبه میانگین نمرات دانشجویان استفاده می‌کند. مثال دیگر به معرفی روش‌های ترکیب عبارات کنترلی برای حل مسئله مشابه است. همچنین به معرفی عملگرهای تخصیص‌دهنده و عملگرهای افزاینده و کاهنده در C++ خواهیم پرداخت. این عملگرها سبب کوتاه شدن عبارات و گاهاً سادگی کار می‌شوند.

2-4 الگوریتم

هر مسئله ‌محاسباتی و کامپیوتری می‌تواند با یک سری از اعمال اجرائی که به ترتیب اجرا می‌شوند، حل شود. از روال‌ها برای حل مسائل کمک گرفته می‌شود و عبارات:
۱- فعالیت‌ها از نوع اجرای هستند و
۲- فعالیت‌ها به ترتیب اجرا می‌شوند،

تعریف الگوریتم می‌باشند. با ذکر مثالی که در زیر آمده می‌توانید ترتیب اجرا و فعالیت‌ها را ببینید و متوجه شوید که ترتیب اجرا چقدر مهم است. الگوریتم “rise – and – shine” در ارتباط با مراحلی است که یک شخص از هنگام برخواستن از خواب تا رفتن به سرکار انجام می‌دهد، مراحل: (۱) برخواستن از تختخواب، (۲) پوشیدن لباس راحتی، (۳) دوش گرفتن، (۴) پوشیدن لباس، (۵) خوردن صبحانه، (۶) رفتن به محل کار. این روتین در مورد نحوه انجام کار و ضوابط تصمیم‌گیری می‌تواند موثر باشد حال اگر ترتیب اجرا به صورت زیر جابجا شود:

(۱) برخواستن از تختخواب، (۲) پوشیدن لباس راحتی، (۳) پوشیدن لباس، (۴) دوش گرفتن، (۵) خوردن صبحانه، (۶) رفتن به محل کار. در این حالت شخص مورد نظر، بایستی به محل کار با لباس خیس برود.
مشخص کردن عبارات اجرائی در یک برنامه کامپیوتری، کنترل برنامه (program control) نامیده شود، که به اجرای صحیح و مرتب عبارات گفته می‌شود.
۳-۴ شبه‌کد

شبه‌کد (pseudocode) یک زبان مصنوعی و فرمال است که به برنامه‌نویس کمک می‌کند تا الگوریتم خود را توسعه دهد. شبه‌کد مخصوصاً برای ایجاد الگوریتم‌های مفید است که می‌خواهیم آنها را تبدیل به برنامه‌های ساخت یافته کنیم شبه‌کد، همانند زبان روزمره انگلیسی است و مزیت آن درک آسان توسط کاربر است، اگر چه جزء زبان‌های برنامه‌نویسی اجرائی نیست. برنامه‌های شبه‌کد توسط کامپیوتر قابل اجرا نیستند. با این همه، شبه‌کدها، می‌توانند قبل از نوشتن یک برنامه به زبان اجرای مانند C++، برنامه‌نویس را در راه صحیح قرار دهند.

شبه‌کدهایی که از آنها استفاده می‌کنیم، فقط از کاراکترها تشکیل شده‌اند و برنامه‌نویس می‌تواند آنها را در یک برنامه ویرایشگر، تایپ کند. شبه‌کد، فقط شامل عبارات اجرائی است. زمانی که شبه‌کد تبدیل به کدهای C++ شود، برنامه حالت اجرایی پیدا خواهد کرد. اعلان‌ها جزء عبارات اجرائی نیستند. برای مثال، اعلان
int i;

فقط به کامپایلر نوع متغیر i را نشان داده و کامپایلر مکانی در حافظه به این متغیر اختصاص می‌دهد. اما این اعلان‌ها هیچ عمل اجرائی نظیر ورودی، خروجی یا یک عمل محاسباتی را در زمانی که برنامه اجرا می‌شود، از خود نشان نمی‌دهند. تعدادی از برنامه‌نویسان لیستی از متغیرها تهیه کرده و منظور از کاربرد هر متغیر را در ابتدای شبه‌کد قرار می‌دهند.

اکنون به مثالی از شبه کد نگاه می‌کنیم که می‌تواند برای کمک به برنامه‌نویس در ایجاد یک برنامه جمع معرفی شده در شکل ۵-۲ (فصل دوم) نوشته شده باشد. این شبه کد (شکل ۱-۴) متناظر با الگوریتمی است که دو عدد صحیح از کاربر دریافت، آنها را با هم جمع و مجموع آنها را به نمایش در می‌آورد. با اینکه لیست کامل شبه کد را به نمایش درآورده‌ایم، اما شما را با نحوه ایجاد یک شبه کد از صورت مسئله آشنا خواهیم کرد.
۱ Prompt the user to enter the first integer
2 Input the first integer
3
4 Prompt the user to enter the second integer
5 Input the second integer
6
7 Add first integer and second integer
8 Display result
شکل ۱-۴ | شبه کد برنامه جمع شکل ۵-۲٫

خطوط ۱-۲ متناظر با عبارات موجود در خطوط ۱۳-۱۴ از شکل ۵-۲ هستند. دقت کنید که عبارات شبه کد، عبارات ساده انگلیسی هستند که هر وظیفه در C++ را بیان می‌کنند. به همین ترتیب، خطوط ۴-۵ متناظر با عبارات موجود در خطوط ۱۶-۱۷ و خطوط ۷-۸ متناظر با عبارات موجود در خطوط ۱۹ و ۲۱ از شکل ۵-۲ هستند.

چندین نکته مهم در شبه کد شکل ۱-۴ وجود دارد. دقت کنید که شبه کد فقط متناظر با کد موجود در تابع main است، به این دلیل که معمولاً از شبه کد برای الگوریتم‌ها استفاده می‌شود، نه برای کل برنامه. در این مورد، از شبه کد برای عرضه الگوریتم استفاده شده است. تابع موجود در این کد به اندازه خود الگوریتم مهم نیست. به همین دلیل، خط ۲۳ از شکل ۵-۲ (عبارت return) در شبه کد وارد نشده است. عبارت return در انتهای هر تابع main قرار دارد و در الگوریتم اهمیتی ندارد. سرانجام، خطوط ۹-۱۱ از شکل ۵-۲ در شبه کد وجود ندارد چرا که اعلان متغیرها جزء عبارات اجرایی نیستند.
۴-۴ عبارات کنترل

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

در دهه ۱۹۶۰، به دلیل وجود مشکلات فراوان در ایجاد نرم‌افزارهای کاربردی، استفاده از روش‌های کنترل انتقال به عنوان یک زمینه بکار گرفته شد و نشانه آن عبارت goto بود. این عبارت به برنامه‌نویس اجازه می‌دهد تا کنترل را به یک مکان ویژه در کل برنامه انتقال دهد. عقیده‌ای که برنامه‌نویسی ساخت یافته نام گرفته بود، تقریباً با برنامه‌نویسی حذف goto یا کاهش آن (goto-less) مترادف شده است.

با تحقیقات Jacopini و Bohm که نشان داد که برنامه‌ها بایستی بدون goto نوشته شوند، دعوت از برنامه‌نویسان به طرف برنامه‌نویسی کاهش استفاده از goto آغاز گردید. اما تا سال ۱۹۷۰ برنامه‌نویسی ساخت یافته جدی گرفته نشد. در نتیجه بکار بردن این روش، توسعه نرم‌افزار و کاهش بودجه‌های ساخت آن مشخص شد. کلید تمام این موفقیت‌ها برنامه‌های ساخت‌یافته‌ای بودند که هم وضوح بالاتر و خطاگیری آسانتری داشتند.
Bohm و Jacopini نشان دادند که تمام برنامه‌ها در سه عبارت کنترلی می‌توانند نوشته شوند:

  • · عبارت توالی (sequence structure)
  • · عبارت انتخاب (selection structure)
  • · عبارت تکرار (repetition structure)
ساختار توالی در C++

ساختار توالی بصورت توکار درC++ وجود دارد. مگر اینکه آن را به نحوه دیگری هدایت کنید. کامپیوتر مبادرت به اجرای عبارات C++ یکی پس از دیگری و به ترتیبی که نوشته شده‌اند می‌کند، که این حالت اجرای ترتیبی یا متوالی است. دیاگرام فعالیت UML (Unified Modeling language) به نمایش درآمده در شکل ۲-۴ نشاندهنده یک ساختار توالی است که در آن دو محاسبه به ترتیب انجام می‌شود. زبان C++ اجازه می‌دهد تا به هر اندازه که لازم داریم از ساختار توالی استفاده کنیم. همانطوری که بزودی مشاهده خواهید کرد، در هر کجا که یک عمل می‌تواند موجود باشد، می‌توانیم چندین عمل را به صورت توالی جایگزین کنیم.

در این شکل، دو عبارت مبادرت به افزودن یک نمره به متغیر مجموع (total) و مقدار ۱ به متغیر counter می‌کنند. چنین عباراتی را می‌توان در یک برنامه محاسبه میانگین نمره چند دانشجو مشاهده کرد. برای محاسبه میانگین، مجموع نمرات به تعداد نمرات تقسیم می‌شود. از متغیر شمارنده (counter) برای نگهداری تعداد نمرات وارد شده استفاده می‌شود. در بخش ۸-۴ با عبارات مشابهی مواجه خواهید شد.

دیاگرام‌های فعالیت بخشی از UML هستند. یک دیاگرام فعالیت مبادرت به مدل‌سازی روندکار (فعالیت) یک بخش از سیستم نرم‌افزاری می‌کند. چنین روندهایی می‌توانند در برگیرنده بخشی از یک الگوریتم، همانند یک ساختار توالی در شکل۲-۴ باشند.دیاگرام‌های فعالیت‌ مرکب از نمادهای معنی‌دار، همانند نمادهای وضعیت عمل (یک مستطیل که گوشه‌های چپ و راست آن به سمت بیرون انحناء داده شده‌اند)، لوزی‌ها و دایره‌های کوچک است. این نمادها توسط فلش‌های انتقال به یکدیگر متصل می‌شوند که نشاندهنده روند فعالیت می‌باشند.

همانند شبه کد، دیاگرام‌های فعالیت به برنامه‌نویسان در توسعه و عرضه الگوریتم‌ها کمک می‌کنند، اگر چه برخی از برنامه‌نویسان شبه کد را ترجیح می‌دهند. دیاگرام‌های فعالیت به وضوح نحوه عملکرد ساختارهای کنترل را نشان می‌دهند.

به دیاگرام فعالیت ساختار متوالی در شکل ۲-۴ توجه کنید. این دیاگرام حاوی دو نماد وضعیت عمل است که نشاندهنده اعمالی هستند که اجرا می‌شوند. هر وضعیت عمل حاوی یک بیان‌کننده عمل است، “add grade to total” و “add 1 to counter” که مشخص‌کننده عملی هستند که انجام خواهند شد. سایر اعمال می‌توانند محاسباتی یا ورودی/ خروجی باشند.
شکل ۲-۴ | دیاگرام فعالیت یک ساختار توالی.
فلش‌‌ها در دیاگرام فعالیت، بنام فلش‌های انتقال‌ شناخته می‌شوند. این فلش‌ها نشاندهنده انتقال هستند و بر این نکته دلالت دارند که ترتیب اجرای اعمال به چه صورتی است. برای مثال در شکل ۲-۴ ابتدا grade با total جمع شده و سپس۱ به counter افزوده می‌شود.

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

همچنین شکل ۲-۴ شامل مستطیل‌های با گوشه‌های خم شده به داخل (سمت راست-بالا) است. این مستطیل‌ها، در UML، نکته (note) نامیده می‌شوند. نکته‌ها توضیحات اضافی در ارتباط با هدف نمادها در دیاگرام ارائه می‌کنند. از نکته‌ها می‌توان در هر دیاگرام UML و نه تنها در دیاگرام‌های فعالیت استفاده کرد. در شکل ۲-۴ از نکته‌های UML برای نمایش کد C++ مرتبط با هر وضعیت عمل در دیاگرام فعالیت استفاده شده است. خط نقطه ‌چین مبادرت به متصل نمودن هر نکته با عنصری می‌کند که در ارتباط با آن توضیح ارائه می‌نماید. معمولاً دیاگرام‌های فعالیت، کدC++ پیاده‌سازی کننده فعالیت را عرضه نمی‌کنند. اما از این نکته‌ها به این منظور در اینجا استفاده کرده‌ایم تا رابطه دیاگرام با کد C++ مربوطه را بهتر نشان دهیم. برای کسب اطلاعات بیشتر در مورد UML به بخش مبحث آموزشی مهندسی نرم‌افزار که در انتهای فصل ۱ الی ۷، ۹، ۱۰، ۱۲ و ۱۳ قرار دارند مراجعه کرده یا از وب سایت http://www.uml.org بازدید نمایید.

عبارات انتخاب در C++

زبان C++ سه نوع عبارت انتخاب تدارک دیده است که در مورد آنها در این فصل و فصل بعدی توضیح خواهیم داد. در عبارت انتخاب if اگر شرط برقرار باشد عبارت یا عبارات داخل بدنه اجرا شده و در صورتیکه شرط برقرار نباشد از روی آن عبارات بدون اجرای آنها عبور خواهد شد. عبارت if..else در صورتیکه شرط برقرار باشد، عبارت (یا دنباله‌ای از عبارات) را انجام داده و در صورتیکه شرط برقرار نباشد، عمل اجرای متفاوتی را به انجام می‌رساند (یا اجرای توالی از عبارات). عبارت switch که در فصل ۵ به بررسی آن خواهیم پرداخت با توجه به ارزش یک عبارت، یکی از چندین عمل اجرائی را به اجرا در می‌آورد.
عبارت if را عبارت تک انتخابی (single-selection) می‌نامند، چرا که یک عمل را انتخاب و اجرا یا آنرا رد می‌کند. عبارت if..else را عبارت دو انتخابی (double-selection) می‌نامند، چرا که انتخابی مابین دو حالت متفاوت انجام می‌دهد. عبارت switch، عبارت چند انتخابی (multiple-selection) نامیده می‌شود، چرا که از میان موارد متفاوت انتخاب خود را انجام می‌دهد.
عبارات تکرار در C++
C++ سه نوع عبارت تکرار بنام‌های زیر تدارک دیده است:

  • while
  • do..while
  • for
عبارت تکرار while، در این فصل معرفی خواهد شد و عبارات do..while، و for در فصل ۵ توضیح داده خواهند شد. کلمات if، else، switch، while، do و for همگی جزء کلمات کلیدی C++ هستند (جدول شکل ۳-۴). با بسیاری از این کلمات کلیدی در این کتاب آشنا خواهید شد.



C++ Keywords auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while C++ only keywords asm bool catch class const_cast delete dynamic_cast explicit false friend inline mutable namespace new operator private protected public reinterpret_cast and static_cast template this throw true try typeid typename using virtual wchar_t and_eq bitand bitor export not not_eq or or_eq xor_eq
شکل ۳-۴ | کلمات کلیدی C++
خلاصه‌ای بر عبارات کنترلی در C++

C++ دارای سه عبارت کنترلی است، که از این به بعد از آنها بعنوان عبارات کنترلی یاد خواهیم کرد: عبارت توالی، عبارات انتخابی (سه نوع- if, if..else و switch) و عبارات تکرار(سه نوع- while, for و do..while). هر برنامه C++ از ترکیب این عبارات کنترلی ایجاد می‌شود. همانند عبارت توالی در شکل ۲-۴، می‌توانیم هر عبارت کنترلی را بصورت یک دیاگرام فعالیت مدل‌سازی کنیم. هر دیاگرام حاوی یک حالت اولیه و یک حالت پایانی است، که به ترتیب نشاندهنده نقطه ورودی (entry point) به عبارت کنترلی و نقطه خروجی (exit point) آن می‌باشند. عبارات کنترلی تک‌ورودی/تک‌خروجی ایجاد آسانتر برنامه‌ها را ممکن می‌سازند. نقطه خروجی یک عبارت کنترلی را می‌توان به نقطه ورودی عبارت کنترلی دیگری متصل کرد و به همین ترتیب ادامه داد. این فرآیند همانند قرار دادن بلوک‌های بر روی هم است، از اینرو این روش، عبارت کنترلی پشته (control structure stacking) نام دارد. این روش یکی از روش‌های موجود برای متصل کردن عبارات کنترلی به یکدیگر است. یک روش دیگر عبارت کنترلی تودرتو یا آشیانه‌ای (control structure nesting) می‌باشد که در آن یک عبارت کنترلی می‌تواند در درون عبارت دیگری قرار گیرد. بنابر این الگوریتم‌ها در برنامه‌های C++ فقط متشکل از سه نوع عبارت کنترلی ترکیب شده با این دو روش هستند.
 
عبارت انتخاب if

در یک عبارت انتخاب، هدف برگزیدن یکی از گزینه‌های موجود برای انجام آن است. برای مثال، فرض کنید که شرط قبولی در یک امتحان نمره ۶۰ است از (۱۰۰). عبارت شبه‌کد آن بصورت زیر می‌باشد:
If student’s grade is greater than or equal to 60
Print “Passed”

شرط “student’s grade is greater than or equal to 60” می‌تواند برقرار باشد یا نباشد. اگر شرط برقرار باشد عبارت “Passed” به معنی قبول شدن به نمایش در می‌آید و عبارت پس از شبه‌کد به ترتیب اجرا می‌شود (بیاد داشته باشید که شبه‌کد یک زبان برنامه‌نویسی واقعی نیست). اگر شرط برقرار نباشد عبارت چاپ نادیده گرفته می‌شود و عبارت شبه‌کد بعدی به ترتیب اجرا خواهد شد. عبارت موجود در بدنه عبارت if رشته Passed را به چاپ می‌رساند. همچنین به دندانه‌دار بودن این عبارت در این عبارت انتخاب دقت کنید. دندانه‌ گذاری امری اختیاری است، اما بکارگیری آن بسیار توصیه می‌شود چرا که ارتباط عبارتهای مختلف برنامه را بخوبی نشان می‌دهند. کامپایلرC++ کاراکترهای whitespace یعنی کاراکترهای فاصله، tab و خطوط جدید بکار رفته در ایجاد دندانه‌ها و فاصله‌گذاری عمودی را بجز کاراکترهای whitespace بکار رفته در رشته‌ها، در نظر نمی‌گیرد.



می‌توان این عبارت شبه‌کد if را در زبان C++ بصورت زیر نوشت
if (grade >= 60 )
cout << “Passed”;
اگر به کد C++ دقت کنید متوجه شباهت نزدیک آن با شبه‌کد خواهید شد و نقش شبه‌کد به عنوان یک ابزار توسعه برنامه بخوبی آشکار می‌شود.

در شکل ۴-۴ دیاگرام فعالیت عبارت تک‌انتخابی if نشان‌داده شده است. این دیاگرام حاوی یکی از مهمترین نمادها در یک دیاگرام فعالیت است. نماد لوزی یا نماد تصمیم نشان می‌دهد که باید در آن نقطه تصمیمی اتخاذ گردد. نماد تصمیم‌گیری بر این نکته دلالت دارد که روند کار در امتداد مسیری به کار ادامه خواهد داد که توسط نماد وابسته نگهبان شرط تعیین می‌شود (آیا شرط برقرار است یا خیر). هر فلش یا بردار انتقال خارج شده از یک نماد تصمیم دارای یک نگهبان شرط است (در درون براکت‌های مربعی در بالا یا کنار فلش انتقال جای می‌گیرد). اگر شرط یک نگهبان شرط برقرار باشد، روند کار وارد وضعیت عملی می‌شود که فلش انتقال به آن اشاره می‌کند. در شکل ۴-۴ اگر grade بزرگتر یا برابر ۶۰ باشد، برنامه کلمه “Passed” را بر روی صفحه نمایش چاپ کرده و سپس انتقال به وضعیت پایانی در این فعالیت می‌رسد. اگر grade کوچکتر از ۶۰ باشد، بلافاصله برنامه به وضعیت پایانی منتقل می‌شود، بدون اینکه پیغامی چاپ کند.
cpp-ch4-4.jpg

شکل ۴-۴ | دیاگرام فعالیت عبارت if.

در فصل اول آموختیم، تصمیم‌گیری می‌تواند براساس شرط‌هایی صورت گیرد که حاوی عملگرهای رابطه‌ای یا برابری هستند. در واقع، در C++ یک شرط می‌تواند بر پایه هر عبارتی ارزیابی گردد، اگر عبارت با صفر ارزیابی شود، با آن همانند false (عدم برقراری شرط) رفتار خواهد شد و اگر عبارت با مقداری غیر از صفر ارزیابی گردد، با آن همانند true (برقراری شرط) رفتار می‌شود. زبان C++ دارای نوع داده بولی(bool) برای متغیرهایی است که فقط قادر به نگهداری مقادیر true و false هستند، که هر دو جزء کلمات کلیدی در C++ می‌باشند.
قابلیت حمل
برای حفظ سازگاری با نسخه‌های قبلی C که از اعداد صحیح برای مقادیر بولی استفاده می‌کردند، می‌توان برای عرضه یک مقدار بولی true از هر مقدار غیرصفری استفاده کرد (معمولاً کامپایلرها از ۱ استفاده می‌کنند). برای عرضه یک مقدار بولی false نیز می‌توان از مقدار صفر استفاده کرد.

دقت کنید که عبارت if یک عبارت تک‌ورودی/ تک‌خروجی است. همچنین دیاگرام‌های مابقی عبارات کنترلی نیز حاوی نمادهای وضعیت اولیه، فلش‌های انتقال، وضعیت اجرا، تصمیم‌گیری و وضعیت پایانی هستند. به این نحوه نمایش عبارات کنترلی روش مدل برنامه‌نویسی اجرائی/تصمیم‌گیری گفته می‌شود.

می‌توانیم هفت صندوق را تصور کنیم که هر کدام فقط حاوی دیاگرام‌های فعالیت UML خالی از یکی از هفت نوع عبارت (ساختار) کنترلی است. وظیفه برنامه‌نویس جفت‌وجور کردن برنامه با سرهمبندی کردن دیاگرام فعالیت به هر تعداد از هر نوع عبارت کنترلی تصریح شده در الگوریتم است که فقط به دو روش قابل انجام است، روش پشته‌ای و تودرتو. در ادامه وضعیت‌های عمل و تصمیم‌گیری را با عبارات اجرایی و نگهبانان شرط به روش مقتضی پر می‌کند. در ارتباط با نوشتن انواع روش‌هایی که می‌تواند در عبارات اجرایی و تصمیم‌گیری بکار گرفته شوند، صحبت خواهیم کرد.



۶-۴ عبارت انتخاب if..else

همانطوری که گفته شد عبارت انتخاب if فقط در صورت برقرار بودن شرط، عملی را به اجرا در می‌آورد، در غیر اینصورت از روی عبارت یا عبارات پرش می‌کند. عبارت انتخاب if..else این امکان را به برنامه‌نویس می‌دهد که تعیین کند چه اعمالی در برقرار بودن شرط اجرا شوند و چه اعمالی در حالت برقرار نبودن شرط به اجرا در آیند. برای مثال، در شبه‌کد زیر
If Student’s grade is greater than or equal to 60
Print “Passed”
Else
Print “Failed”
اگر نمره دانش‌آموز برابر ۶۰ یا بالاتر باشد، عبارت “Passed” به نمایش در می‌آید و اگر کمتر از آن باشد عبارت “Failed“. در هر دو حالت پس از انجام عمل چاپ، عبارت شبه‌کد بعدی به اجرا گذاشته خواهد شد.
عبارت شبه‌کد if..else مطرح شده را می‌توان در زبان C++ و به فرم زیر نوشت:
if (grade >= 60 )
cout << “Passed”;
else
cout << “Failed”;
به دندانه‌دار بودن بدنه شرط else دقت کنید که با خطوط بالای خود در شرط if یکسان قرار گرفته‌اند.

در شکل ۵-۴ روند کنترل جریان در یک عبارت (ساختار) if..else نشان داده شده است. مجدداً توجه کنید (در کنار وضعیت اولیه، فلش‌های انتقال و وضعیت پایانی) که نمادهای بکار رفته در این دیاگرام فعالیت عبارتند از نمادهای عمل و تصمیم‌گیری و تاکید ما بر مدل‌سازی عمل/ تصمیم‌گیری است. مجدداً به صندو‌ق‌های خالی از دیاگرام‌های فعالیت عبارات انتخاب دوگانه فکر کنید که برنامه‌نویس می‌تواند به روش پشته‌ای یا تودرتو با سایر دیاگرام‌های فعالیت ساختارهای کنترلی بکار گیرد تا مبادرت به پیاده‌سازی الگوریتم کند.
cpp-ch4-5.jpg

شکل ۵-۴ دیاگرام فعالیت عبارت دو انتخابی if..else
عملگر شرطی (?:)
زبان C++ حاوی عملگر شرطی (?:)، است که قرابت نزدیکی با عبارت if..else دارد. عملگر شرطی C++ تنها عملگر ternary است، به این معنی که سه عملوند دریافت می‌کند. عملوندها به همراه عملگر شرطی تشکیل عبارت شرطی را می‌دهند. عملوند اول نشاندهنده شرط می‌باشد، عملوند دوم مقداری است که در صورت true بودن شرط انتخاب می‌شود و عملوند سوم مقداری است که در صورت برقرار نبودن شرط یا false بودن آن انتخاب می‌شود. برای مثال عبارت زیر
cout << (grade >= 60 ? ”Passed” : ”Failed” );
حاوی یک عبارت شرطی، است که در صورت برقرار بودن شرط grade >= 60 رشته “Passed” ارزیابی می‌شود، اما اگر شرط برقرار نباشد، رشته “Failed” بکار گرفته خواهد شد. از اینرو عملکرد این عبارت شرطی دقیقا همانند عملکرد عبارت if..else قبلی است. عملگر شرطی از تقدم پایین‌تری برخوردار است و از اینرو معمولا کل عبارت شرطی را در درون پرانتزها قرار می‌دهند.
مقادیر موجود در یک عبارت شرطی قادر به اجرا شدن نیز هستند. برای مثال عبارت شرطی زیر مبادرت به چاپ “Passed” یا “Failed” می‌کند.
grade >= 60 ? cout << “Passed” : cout << “Failed”;
این عبارت به صورت زیر تفسیر می‌شود «اگر grade بزرگتر یا مساوی ۶۰ باشد، پس cout<<”Passed”، در غیر اینصورت “.cout<<”Faild” همچنین این عبارت قابل مقایسه با عبارت if..else قبلی است. عبارات شرطی را می‌توان در مکان‌های از برنامه‌ که امکان استفاده از if..else وجود ندارد، بکار گرفت.
عبارات تودرتوی if..else

عبارت تودرتوی if..else برای تست چندین شرط با قرار دادن عبارتهای if..else در درون عبارتهای if..else دیگر است. برای مثال، عبارت شبه‌کد زیر، حرف “A” را برای نمره‌های بزرگتر یا برابر ۹۰، “B” را برای نمره‌های در محدودة ۸۰-۸۹، “C” را برای نمره‌های در محدودة ۷۰-۷۹، “D” را برای نمره‌های در محدودة ۶۰-۶۹ و “F” را سایر نمرات به چاپ می‌رساند.
If student’s grade is greater then or equal to 90 Print “A”
Else
If student’s grade is greater than or equal to 80
Print “B”
Else
If student’s grade is greater than or equal to 70
Print “C”
Else
If student’s grade is greater than or equal to 60
Print “D”
Else
Print “F”
عبارت شبه‌کد بالا را می‌توان در زبان C++ و به فرم زیر نوشت:
if (studentGrade>=90) // 90 and above gets “A”
cout << “A”;
else
if (studentGrade>=80) // 80-89 gets “B”
cout << “B”;
else
if (strudentGrade>=70) // 70-79 gets “C”
cout << “C”;
else
if (studentGrade>=60) // 60-69 gets “D”
cout << “D”;
else // less than 60 gets “F”
cout << “F”;

اگر مقدار studentGrade بزرگتر یا مساوی ۹۰ باشد، اولین شرط از پنج شرط برقرار شده و فقط عبارت cout قرار گرفته در بدنه اولین شرط به اجرا در می‌آید. پس از اجرای این عبارت از بخش else خارجی عبارت if..else عبور خواهد شد.
اکثر برنامه‌نویسان C++ ترجیح می‌دهند که عبارت if..else را با استفاده از کلمه کلیدی else if و بصورت زیر در برنامه‌های خود بنویسند:
if (studentGrade>=90) // 90 and above gets “A”
cout << “A”;
else if (studentGrade>=80) // 80-89 gets “B”
cout << “B”;
else if (studentGrade>=70) // 70-79 gets “C”
cout << “C”;
else if (studentGrade>=60) // 60-69 gets “D”
cout << “D”;
else // less than 60 gets “F”
cout << “F”;

هر دو حالت معادل یکدیگرند، اما نوع آخر در نزد برنامه‌نویسان از محبوبیت بیشتری برخوردار است. چرا که از دندانه‌دار کردن عمیق کد به طرف راست اجتناب می‌شود.
مشکل dangling-else
همیشه کامپایلر C++ یک else را با یک if در نظر می‌گیرد، مگر اینکه خلاف آنرا با استفاده از براکت‌ها مشخص کنید. به این مشکل dangling-else می‌گویند. برای مثال،
if ( x > 5 )
if ( y > 5 )
cout << “x and y are > 5″;
else
cout << “x is <=5″;
به نظر می‌رسد بر این نکته دلالت دارد که اگر x بزرگتر از ۵ باشد، عبارت if تودرتو تعیین می‌کند که آیا y نیز بزرگتر از ۵ است یا خیر. اگر چنین باشد، رشته “x and y are > 5″ در خروجی چاپ می‌شود. در غیر اینصورت اگر x بزرگتر از ۵ نباشد، بخش else از عبارت if..else رشته “x is <=5″ را چاپ خواهد کرد.
با این همه امکان دارد عبارت if تودرتوی فوق مطابق با انتظار کار نکند. تفسیر کامپایلر از عبارت بصورت زیر خواهد بود
if ( x > 5 )
if ( y > 5 )
cout << “x and y are > 5″;
else
cout << “x is <=5″;

که در آن بدنه اولین عبارت if یک عبارت if..else تودرتو است. این عبارت مبادرت به تست بزرگتر بودن x از ۵ می‌کند. اگر چنین باشد، اجرا با تست y بزرگتر از ۵ ادامه می‌یابد. اگر شرط دوم برقرار باشد، رشته “x and y are >5″ به نمایش در خواهد آمد. با این همه اگر شرط دوم برقرار نباشد، رشته ”x is <=5″ به نمایش در می‌آید، حتی اگر بدانیم که x بزرگتر از ۵ است.
برای اینکه عبارت فوق بنحوی کار کند که از انتظار داریم، بایستی کل عبارت بصورت زیر نوشته شود:
if ( x > 5 )
{
if ( y > 5 )
cout << “x and y are > 5″;
}
else
cout << “x is <=5″;
براکت‌ها به کامپایلر نشان می‌دهند که دومین if در بدنه اولین if قرار دارد و else در ارتباط با اولین if می‌باشد.
بلوک‌ها
معمولا عبارت انتخاب if فقط منتظر یک عبارت در بدنه خود است. به همین ترتیب، هر یک از بخش‌های else و if در یک عبارت if..else انتظار مقابله با یک عبارت در بدنه خود را دارند. برای وارد کردن چندین عبارت در بدنه یک if یا در بخش‌های if..else ، عبارات را در درون براکت‌ها ( { } ) قرار دهید. به مجموعه‌ای از عبارات موجود در درون یک جفت براکت، بلوک می‌گویند.
مثال زیر شامل یک بلوک در بخشی از else یک عبارت if..else است.
if (studentGrade>=60)
cout << “Passed.\n”;
else
{
cout << “Failed.\n”;
cout << “You must take this course again.\n”
}
در این مورد، اگر studentGrade کمتر از ۶۰ باشد، برنامه هر دو عبارت موجود در بدنه else را اجرا کرده و پیغام‌های زیر را چاپ می‌کند.
Failed
You must take this course agein.
به براکت‌های احاطه‌کننده دو عبارت در ضابطه else دقت کنید. این براکت‌ها مهم هستند. بدون این براکت‌ها، عبارت
cout << “You must take this course again.\n”;
در خارج از بدنه بخش else قرار می‌گیرد و صرفنظر از اینکه شرط برقرار باشد یا خیر، اجرا خواهد شد. این مثال نمونه‌ای از یک خطای منطقی است.
 
بالا