نحوه یافتن فایلهای سرآیند
توجه کنید که نام فایل سرآیند 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 نشان می دهد که تابع نیازمند پارامتر نبوده و رشته برگشت میدهد (خط ۱۴).
شکل ۱۱-۳ | تعریف کلاس 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 اعلان شدهاند.
شکل ۱۲-۳ | تعاریف تابع عضو GradeBook نشاندهنده ساختار پیادهسازی کلاس GradeBook.
تست کلاس GradeBook
برنامه شکل ۱۳-۳ همان کار دستکاری شی GradeBook بکار رفته در برنامه ۱۰-۳ را انجام میدهد. جداسازی واسط GradeBook از بخش پیادهسازی توابع عضو تاثیری در روش استفاده این کد سرویسگیرنده از کلاس ندارد و فقط بر نحوه کامپایل برنامه و لینک آن تاثیر دارد که در مورد آن صحبت خواهیم کرد.
شکل ۱۳-۳ | کلاس GradeBook پس از جداسازی واسط از پیادهسازی.
همانند برنامه شکل ۱۰-۳، خط ۸ برنامه شکل ۱۳-۳ شامل فایل سرآیند GradeBook.h است و از اینرو است که کامپایلر میتواند از ایجاد و دستکاری صحیح شی های GradeBook در کد سرویسگیرنده مطمئن گردد. قبل از اجرای این برنامه، باید فایلهای کد منبع در شکل ۱۲-۳ و ۱۳-۳ هر دو کامپایلشده و سپس به هم لینک گردند. فراخوانی تابع عضو در کد سرویسگیرنده نیازمند گره خوردن با پیادهسازی توابع عضو کلاس دارد، کاری که لینکر آن را انجام میدهد.
سلام
میشه آموزش ++c رو تو سایتتون بزارید؟
مرسی
گذاشتم رو سایت که؟ ایا منظورتون به صورت تصویری هستش؟
very good
سلام کمکککککک:
آقا من این سه تا فایل رو درست صحیح دارم
ماما نمیتونم با استفاده از dev c++ کامپايلش کنم ...
کامپايل کردنش فرآیند خاصی داره؟؟
لطفاااااااااااااا اطلاع بدین بهم سه روزه درگيرشم
حیف نیست این مقاله به این خوبی عکس هاش دیده نمیشه خواهشمند است عکس ها رو تو یه سرور قوی تر قرار بدین با تشمر