کلاس ها و برنامه نویسی شیءگرا
در قسمت های قبلی ، چند برنامه ساده ايجاد كرديم كه ميتوانستند پيغامهاي را به كاربر نشان داده، اطلاعاتي از وي دريافت نمايند، محاسباتي انجام داده و تصميمگيري كنند. در اين فصل، شروع به نوشتن برنامههايي ميكنيم كه مفاهيم پايه برنامهنويسي شيگراي معرفي شده در بخش 17-1 را بكار ميگيرند. يكي از ويژگيهاي مشترك در هر برنامه فصل دوم اين است كه تمام عبارات كه كاري انجام ميدهند در تابع
main
جاي داده شدهاند. بطور كلي، برنامههايي كه در اين كتاب ايجاد ميكنيد، متشكل از تابع
main
و يك يا چند كلاس كه هر يك حاوي اعضاي داده و توابع عضو هستند، خواهند بود. اگر شما عضوي از تيم توسعه (طراحي) در يك مجموعه حرفهاي هستيد، احتمال دارد بر روي سيستمهاي نرمافزاري كه حاوي صدها، يا هزاران كلاس است، كار كنيد. در اين فصل، هدف ما توسعه يك چهارچوب كاري ساده خوشفرم به لحاظ مهندسي نرمافزار به منظور سازماندهي برنامههاي شيگرا در
C++
است.
ابتدا توجه خود را به سمت كلاسهاي موجود در دنياي واقعي متمركز ميكنيم. سپس به آرامي به سمت دنبالهاي از هفت برنامه كامل ميرويم كه به توصيف نحوه ايجاد و استفاده از كلاسهاي متعلق به خودمان ميپردازند. اين مثالها با مبحث آموزشي هدفمند كه بر توسعه كلاس
grade-book
تمركز دارد، شروع ميشود و مربي ميتواند از آن براي نگهداري امتيازات دانشجو استفاده كند. اين مبحث آموزشي در چند فصل آتي بهبود خواهد يافت و در فصل هفتم بحد اعلي خود خواهد رسيد.
2-3 كلاسها، شيها، توابع عضو و داده
اجازه دهيد بحث را با يك مقايسه ساده كه در افزايش درك شما از مطالب بخش 17-1 موثر است آغاز كنيم. فرض كنيد كه ميخواهيد با اتومبيلي رانندگي كرده و با فشردن پدال گاز آن را سريعتر به حركت در آوريد. چه اتفاقي قبل از اينكه بتوانيد اينكار را انجام دهيد، بايد رخ دهد؟ بسيار خوب، قبل از اينكه بتوانيد با اتومبيلي رانندگي كنيد، بايد كسي آن را طراحي و ساخته باشد. معمولاً ساخت اتومبيل، با ترسيم يا نقشهكشي مهندسي شروع شود. همانند طراحي صورت گرفته براي خانه. اين ترسيمات شامل طراحي پدال گاز است كه راننده با استفاده از آن سبب ميشود تا اتومبيل سريعتر حركت كند. تا حدي، پدال سبب «پنهان» شدن پيچيدگي مكانيزمي ميشود كه اتومبيل را سريعتر بحركت در ميآورد، همانطوري كه پدال ترمز سبب «پنهان» شدن مكانيزمي ميشود كه از سرعت اتومبيل كم ميكند، فرمان اتومبيل سبب «پنهان» شدن مكانيزمي ميشود كه اتومبيل را هدايت ميكند و موارد ديگر. با انجام چنين كارهايي، افراد عادي ميتوانند به آساني اتومبيل را هدايت كرده و براحتي از پدال گاز، ترمز و فرمان، مكانيزم تعويض دنده و ساير «واسطهاي» كاربرپسند و ساده استفاده كنند تا پيچيدگي مكانيزمهاي داخلي اتومبيل براي راننده مشخص نباشد.
متاسفانه، نميتوانيد با نقشههاي ترسيمي يك اتومبيل رانندگي كنيد، قبل از اينكه با اتومبيلي رانندگي كنيد بايد، آن اتومبيل از روي نقشههاي ترسيمي ساخته شود. يك اتومبيل كاملاً ساخته شده داراي پدال گاز واقعي براي به حركت درآوردن سريع اتومبيل است. اما اين هم كافي نيست، اتومبيل بخودي خود شتاب نميگيرد، از اينرو لازم است راننده بر روي پدال گاز فشار آورد تا به اتومبيل دستور حركت سريعتر را صادر كند.
حال اجازه دهيد تا از مثال اتومبيل مطرح شده براي معرفي مفاهيم كليدي برنامهنويسي شيگرا در اين بخش استفاده كنيم. انجام يك وظيفه در يك برنامه مستلزم يك تابع (همانند
main
كه فصل دوم توضيح داده شده) است. تابع، توصيفكننده مكانيزمي است كه وظيفه واقعي خود را به انجام ميرساند. تابع پيچيدگي وظايفي كه قرار است انجام دهد از ديد كاربر خود پنهان ميسازد، همانند پدال گاز در اتومبيل كه پيچيدگي مكانيزم شتابگيري را از ديد راننده پنهان ميكند. در
C++
، كار را با ايجاد واحدي بنام كلاس كه خانه تابع محسوب ميشود، آغاز ميكنيم، همانند نقشه ترسيمي اتومبيل كه طرح پدال گاز نيز در آن قرار دارد. از بخش 17-1 بخاطر داريد كه به تابع متعلق به يك كلاس، تابع عضو گفته ميشود. در يك كلاس ميتوان يك يا چند تابع عضو داشت كه براي انجام وظايف كلاس طراحي شدهاند. براي مثال، يك كلاس ميتواند نشاندهنده يك حساب بانكي و حاوي يك تابع عضو براي ميزان سپرده در حساب، تابع ديگري براي ميزان برداشت پول از حساب و تابع سومي هم براي نمايش ميزان پول موجود در حساب باشد.
همانطوري كه نميتوانيد با نقشه ترسيمي اتومبيل رانندگي كنيد، نميتوانيد كلاسي را مشتق كنيد. همانطوري كه بايد قبل از اينكه بتوانيد با اتومبيلي رانندگي كنيد، شخص از روي نقشه ترسيمي مبادرت به ساخت اتومبيل كرده باشد، شما هم بايد قبل از اينكه برنامه بتواند وظايف توصيفي در كلاس را بدست آورد، بايد يك شي از كلاس را ايجاد كرده باشيد. اين يكي از دلايل شناخته شدن
C++
بعنوان يك زبان برنامهنويسي شيگرا است. همچنين توجه نمائيد كه همانطوري كه ميتوان از روي نقشه ترسيمي اتومبيلهاي متعددي ساخت، ميتوان از روي يك كلاس، شييهاي متعددي ايجاد كرد.
زمانيكه رانندگي ميكنيد، با فشردن پدال گاز، پيغامي به اتومبيل ارسال ميشود كه وظيفهاي را انجام دهد، كه اين وظيفه افزودن سرعت اتومبيل است. به همين ترتيب، پيغامهايي را به يك شي ارسال ميكنيم، هر پيغام بعنوان فراخوان يك تابع عضو شناخته ميشود و به تابع عضو از شيي اعلان ميكند كه وظيفه خود را انجام دهد. اينكار غالباً بعنوان تقاضاي سرويس از يك شي شناخته ميشود.
تا بدينجا، از مقايسه اتومبيل براي توضيح كلاسها، شيها و توابع عضو استفاده كرديم. علاوه بر قابليتهاي اتومبيل، هر اتومبيلي داراي چندين صفت است، صفاتي همانند رنگ، تعداد درها، ظرفيت باك، سرعت جاري و مسافت طي شده. همانند قابليتهاي اتومبيل، اين صفات هم بعنوان بخشي از طراحي اتومبيل در نقشه ترسيمي ديده ميشوند. همانطوري كه رانندگي ميكنيد، اين صفات هميشه در ارتباط با اتومبيل هستند. هر اتومبيلي، حافظ صفات خود است. براي مثال، هر اتومبيلي از ميزان سوخت موجود در باك خود مطلع است، اما از ميزان سوخت موجود در باك ساير اتومبيلها مطلع نيست. به همين ترتيب، يك شي داراي صفاتي است كه به همراه شي بوده و در برنامه بكار گرفته ميشوند.
اين صفات به عنوان بخشي از كلاس شي تصريح ميشوند. براي مثال، يك شي حساب بانكي داراي صفت موجودي است كه نشاندهنده مقدار پول موجود در حساب ميباشد. هر شي حساب بانكي از ميزان موجودي در حساب خود مطلع است، اما از موجودي ساير حسابها در بانك اطلاعي ندارد. صفات توسط اعضاي داده كلاس مشخص ميشوند.
3-3 نگاهي بر مثالهاي اين فصل
در مابقي اين فصل مبادرت به معرفي هفت مثال ساده ميكنيم كه به توصيف مفاهيم معرفي شده در ارتباط با قياس اتومبيل ميپردازند. خلاصهاي از اين مثالها در زير آورده شده است. ابتدا مبادرت به ايجاد كلاس
GradeBook
ميكنيم تا قادر به توصيف اين مفاهيم باشيم:
1- اولين مثال نشاندهنده كلاس
GradeBook
با يك تابع عضو است كه فقط يك پيغام خوشآمدگويي را در زمان فراخواني به نمايش در ميآورد. سپس شما را با نحوه ايجاد يك شي از اين كلاس و فراخواني تابع عضو كه پيغام خوشآمدگويي را ظاهر ميسازد، آشنا خواهيم كرد.
2- دومين مثال، اصلاح شده مثال اول است. در اين مثال به تابع عضو اجازه داده ميشود تا نام دوره را به عنوان يك آرگومان قبول كند. سپس، تابع عضو مبادرت به نمايش نام دوره بعنوان بخشي از پيغام خوشآمدگويي ميكند.
3- سومين مثال نحوه ذخيرهسازي نام دوره در يك شي
GradeBook
را نشان ميدهد. براي اين نسخه از كلاس، شما را با نحوه استفاده از توابع عضو به منظور تنظيم نام دوره در شي و بدست آوردن نام دوره از شي آشنا ميكنيم.
4- چهارمين مثال به توصيف نحوه مقداردهي اوليه داده در شي
GradeBook
به هنگام ايجاد شي ميپردازد. مقداردهي اوليه توسط يك تابع عضو بنام سازنده كلاس صورت ميگيرد. همچنين اين مثال نشان ميدهد كه هر شي
GradeBook
از نام دورة خود نگهداري ميكند.
5- مثال پنجم اصلاح شده مثال چهارم است كه به توصيف نحوه قرار دادن كلاس
GradeBook
در يك فايل مجزا به منظوراستفاده مجدد از نرمافزار، ميپردازد.
6- مثال ششم، تغييريافته مثال پنجم است كه حاوي يك اصل مهندسي نرمافزار در جداسازي بخش واسط كلاس از بخش پيادهسازي آن است. با انجام اينكار فرآيند اصلاح و تغيير آسانتر ميشود بدون اينكه در سرويسگيرندههاي كلاس تاثيرگذار باشد.
7- آخرين مثال قابليت كلاس
GradeBook
را با معرفي اعتبارسنجي داده افزايش خواهد داد. اعتبارسنجي ما را از فرمت صحيح داده در يك شي مطمئن ميسازد. براي مثال يك شي
Date
نيازمند يك مقدار در محدودة
1
الي
12
است. در اين مثال
GradeBook
، تابع عضوي كه مبادرت به تنظيم نام دورة براي يك شي
GradeBook
ميكند، سبب ميشود تا نام دوره
25
كاراكتر يا كمتر باشد. اگر طول نام دوره بيش از
25
كاراكتر باشد، تابع عضو فقط از
25
كاراكتر اول استفاده كرده و يك پيغام خطا به نمايش در ميآورد.
توجه كنيد كه مثالهاي
GradeBook
در اين فصل فرآيند واقعي ذخيرهسازي را انجام نميدهند. در فصل چهارم، فرآيند پردازش امتيازات را با
GradeBook
و در فصل هفتم مبادرت به ذخيرهسازي اطلاعات در يك شي
GradeBook
خواهيم كرد.
4-3 تعريف كلاس با يك تابع عضو
كار را با مثالي آغاز ميكنيم (شكل 1-3) كه حاوي كلاس
GradeBook
است و ميتوان از آن براي نگهداري امتيازات دانشجويان استفاده كرد. تابع
main
در خطوط
20-25
مبادرت به ايجاد يك شي
GradeBook
كرده است. اين برنامه نسخه اول بوده و نسخه كامل آن در فصل هفتم ايجاد خواهد شد. تابع
main
از اين شي و تابع عضو آن براي نمايش پيغام بر روي صفحه نمايش (پيغام خوشآمدگويي) استفاده ميكند.
1
// Fig. 3.1: fig03_01.cpp
2
// Define class GradeBook with a member function displayMessage;
3
// Create a GradeBook object and call its displayMessage function.
4
#include <iostream>
5
using std::cout;
6
using std::endl;
7
8
// GradeBook class definition
9
class GradeBook
10
{
11
public:
12
// function that displays a welcome message to the GradeBook user
13
void displayMessage()
14
{
15
cout << "Welcome to the Grade Book!" << endl;
16
} // end function displayMessage
17
}; // end class GradeBook
18
19
// function main begins program execution
20
int main()
21
{
22
GradeBook myGradeBook; // create a GradeBook object named myGradeBook
23
myGradeBook.displayMessage(); //
call object's displayMessage function
24
return 0; // indicate successful termination
25
} // end main
Welcome to the Grade Book!
|
شكل 1-3 | تعريف كلاس
GradeBook
با يك تابع عضو، ايجاد يك شي
GradeBook
و فراخواني تابع عضو آن.
ابتدا به توصيف نحوه تعريف يك كلاس و تابع عضو ميپردازيم. سپس به توضيح نحوه ايجاد يك شي و نحوه فراخواني تابع عضو يك شي ميپردازيم. چند مثال اول حاوي تابع
main
و كلاس
GradeBook
است كه از آن در همان فايل استفاده ميكند. در انتهاي اين فصل، به معرفي روشهاي خبره در ساختارمند كردن برنامهها به منظور افزايش كارايي و انجام بهتر مهندسي نرمافزار خواهيم پرداخت.
كلاس
GradeBook
قبل ا
ز اينكه تابع
main
(خطوط
20-25
) بتواند شي از كلاس
GradeBook
ايجاد كند، بايستي به كامپايلر توابع عضو و اعضاي داده متعلق به كلاس را اعلان كنيم. اين كار بنام تعريف كلاس شناخته ميشود. تعريف كلاس
GradeBook
(خطوط
9-17
) حاوي تابع عضوي بنام
displayMessage
است (خطوط
13-16
) كه پيغامي بر روي صفحه نمايش چاپ ميكند (خط
15
). بخاطر داريد كه يك كلاس همانند يك نقشه ترسيمي است، از اينرو نياز داريم كه يك شي از كلاس
GradeBook
ايجاد كنيم (خط
22
)، و تابع عضو
displayMessage
آن را فراخواني نمايم (خط
23
) تا خط
15
را به اجرا درآورده و پيغام خوشآمدگويي را به نمايش درآورد. بزودي به توضيح دقيقتر خطوط
22-23
خواهيم پرداخت.
تعريف كلاس از خط
9
و با كلمه كليدي
class
و بدنبال آن نام كلاس
GradeBook
آغاز شده است. بطور قراردادي، نام كلاسهاي تعريف شده توسط كاربر با حروف بزرگ شروع ميشوند و در صورت وجود كلمات بعدي، ابتداي آنها هم با حروف بزرگ آغاز ميگردد. به اين روش غالباً روش
camel
case
ميگويند.
بدنه هر كلاسي در بين جفت براكت باز و بسته (
}
و
{
) محدود ميشود، همانند خطوط
10
و
17
. تعريف كلاس با يك سيمكولن خاتمه ميپذيرد (خط
17
).
خطاي برنامهنويسي
فراموش كردن سيمكولن در انتهاي تعريف يك كلاس، خطاي نحوي بدنبال خواهد داشت.
بخاطر داريد كه تابع
main
هميشه و بصورت اتوماتيك به هنگام اجراي برنامه فراخواني ميشود. اكثر توابع بصورت اتوماتيك فراخواني نميشوند. همانطوري كه مشاهده خواهيد كرد، بايستي تابع عضو
displayMessage
را بطور صريح فراخواني كنيد تا وظيفه خود را انجام دهد.
خط
14
حاوي برچسب تصريحكننده دسترسي
public:
است. كلمه كليدي
public
، تصريحكننده دسترسي ناميده ميشود. خطوط
13-16
تعريفكننده تابع عضو
displayMessage
هستند. اين تابع عضو پس از
public:
قرار دارد تا نشان دهد كه تابع «بصورت سراسري در دسترس است»، به اين معني كه ميتواند توسط ساير توابع موجود در برنامه و توسط توابع عضو كلاسهاي ديگر فراخواني گردد. هميشه در مقابل تصريحكنندههاي دسترسي يك كولن (
:
) قرار داده ميشود. براي مابقي متن، زمانيكه به تصريحكننده دسترسي
public
اشاره ميكنيم، كولن آن را در نظر نميگيريم. در بخش 6-3 به معرفي دومين تصريحكننده دسترسي بنام
private
خواهيم پرداخت.
هر تابعي در برنامه وظيفهاي را انجام داده و امكان دارد مقداري را پس از كامل كردن وظيفه خود برگشت دهد. براي مثال، تابعي ميتواند يك محاسبه انجام داده و سپس نتيجه آن محاسبه را برگشت دهد. زمانيكه تابعي تعريف ميشود، بايد نوع برگشتي آن را مشخص سازيد، تا تابع پس از اتمام كار، آن نوع را برگشت دهد. در خط
13
، كلمه كليدي
void
قرار گرفته است (در سمت چپ نام تابع
displayMessage
) و نشاندهندة نوع برگشتي از سوي تابع است. نوع برگشتي
void
بر اين نكته دلالت دارد كه
displayMessage
وظيفهاي را انجام خواهد داد، اما دادهاي را به فراخوان خود (در اين مثال، تابع فراخوان،
main
ميباشد) پس از اتمام وظيفه خود برگشت نخواهد داد. (در شكل 5-3 مثالي را مشاهده ميكنيد كه تابع در آن مقداري را برگشت داده است).
نام تابع عضو
displayMessage
بوده و قبل از آن نوع برگشتي قرار دارد. بطور قراردادي، اسامي توابع با يك حرف كوچك شروع شده و تمام كلمات متعاقب آن با حرف بزرگ شروع ميشوند. پرانتزهاي قرار گرفته پس از نام تابع عضو، بر اين نكته دلالت دارند كه اين يك تابع است. يك جفت پرانتز خالي، همانند خط
13
، نشان ميدهد كه اين تابع عضو، نيازي به داده اضافي براي انجام وظيفه خود ندارد. در بخش 5-3 شاهد تابع عضوي خواهيد بود كه براي انجام وظيفه خود نيازمند داده اضافي است. معمولاً به خط
13
، سرآيند تابع گفته ميشود. بدنه هر تابعي با براكتهاي باز و بسته مشخص ميشود (
}
و
{
)، همانند خطوط
14
و
16
.
بدنه تابع حاوي عباراتي است كه وظيفه تابع را به انجام ميرسانند. در اين مورد، تابع عضو
displayMessage
حاوي يك عبارت است (خط
15
) كه پيغام
"Welcome to the Grade Book!"
را به نمايش در ميآورد. پس از اجراي اين عبارت، تابع وظيفه خود را به انجام رسانده است.
خطاي برنامهنويسي
برگشت دادن مقداري از يك تابع كه نوع برگشتي آن بصورت
void
اعلان شده است، خطاي كامپايلر بدنبال خواهد داشت.
خطاي برنامهنويسي
تعريف تابعي در درون تابع ديگر، خطاي نحوي است.
تست كلاس
GradeBook
در اين مرحله ميخواهيم كه از كلاس
GradeBook
در برنامه استفاده كنيم. همانطوري كه در فصل دوم آموختيد، تابع
main
با اجراي هر برنامهاي شروع بكار ميكند. خطوط
20-25
از برنامه شكل 1-3 حاوي تابع
main
هستند، كه اجراي برنامه را تحت كنترل دارد.
در اين برنامه، مايل هستيم تابع عضو
displayMessage
از كلاس
GradeBook
براي نمايش پيغام خوشآمدگويي اجرا گردد (فراخواني شود). اصولاً تا زمانيكه شيي از يك كلاس ايجاد نكرده باشيد، نميتوانيد تابع عضو كلاس را فراخواني نمائيد (در بخش 7-10 با تابع عضو
static
، آشنا خواهيد شد كه در اين مورد يك استثناء ميباشد.) خط
22
يك شي از كلاس
GradeBook
بنام
myGradeBook
ايجاد ميكند. دقت كنيد كه نوع متغير
GradeBook
است، كلاس تعريف شده در خطوط
9-17
. زمانيكه متغيرهايي از نوع
int
اعلان ميكنيم، همانند كاري كه در فصل دوم انجام داديم، كامپايلر از مفهوم
int
مطلع است و ميداند كه آن يك نوع بنيادين ميباشد. با اين وجود، زمانيكه مبادرت به نوشتن خط
22
ميكنيم، كامپايلر بصورت اتوماتيك از مفهوم نوع
GradeBook
اطلاعي ندارد و آن را نميشناسد، اين نوع يك نوع تعريف شده توسط كاربر است. از اينرو، بايد به كامپايلر، با قرار دادن تعريف كلاس همانند كاري كه در خطوط
9-17
انجام دادهايم،
GradeBook
را معرفي كنيم. اگر اين خطوط را حذف كنيم، كامپايلر پيغام خطايي همانند
" 'GradeBook' : undeclared identifier"
را در
GNU C++
صادر ميكند. هر كلاس جديدي كه ايجاد ميكنيد، تبديل به يك نوع جديد ميشود كه ميتواند براي ايجاد شيها بكار گرفته شود. برنامهنويسان ميتوانند در صورت نياز مبادرت به تعريف كلاسهاي با نوع جديد نمايند، و اين يكي از دلايل شناخته شدن
C++
بعنوان يك زبان بسطپذير است.
خط
23
مبادرت به فراخواني تابع عضو
displayMessage
(تعريف شده در خطوط
13-16
) با استفاده از متغير
myGradeBook
و بدنبال آن عملگر نقطه (
.
)، سپس نام تابع
displayMessage
و يك جفت پرانتز خالي ميكند. اين فراخواني موجب ميشود كه تابع
displayMessage
براي انجام وظيفه خود فراخواني شود. در ابتداي خط
23
، عبارت
"myGradeBook."
بر اين نكته دلالت دارد كه
main
بايستي از شي
GradeBook
كه در خط
22
ايجاد شده است، استفاده نمايد. پرانتزهاي خالي در خط
13
نشان ميدهند كه تابع عضو
displayMessage
نيازي به داده اضافي براي انجام وظيفه خود ندارد. (در بخش 5-3، با نحوه ارسال داده به تابع آشنا خواهيد شد) زمانيكه
displayMessage
وظيفه خود را انجام داد، تابع
main
به اجراي خود از خط
24
ادامه خواهد داد، كه نشان ميدهد
main
وظيفه خود را به بدرستي انجام داده است. با رسيدن به انتهاي
main
، برنامه خاتمه ميپذيرد.
دياگرام كلاس
UML
براي كلاس
GradeBook
از بخش 17-1 بخاطر داريد كه
UML
يك زبان گرافيكي بكار رفته توسط برنامهنويس براي نمايش سيستمهاي شيگرا به يك روش استاندارد است. در
UML
، هر كلاس در يك دياگرام كلاس و بصورت يك مستطيل با سه قسمت (بخش) مدلسازي ميشود. شكل 2-3 نشاندهنده يك دياگرام كلاس
UML
براي كلاس
GradeBook
معرفي شده در برنامه 1-3 است. بخش فوقاني حاوي نام كلاس است، كه در وسط قرار گرفته و بصورت توپر نوشته ميشود. بخش مياني، حاوي صفات كلاس است كه مرتبط با اعضاي داده در
C++
است. در شكل 2-3 بخش مياني خالي است، چرا كه اين نسخه از كلاس
GradeBook
در برنامه 1-3 داراي صفات نيست. (در بخش 6-3 نسخهاي از كلاس
GradeBook
عرضه شده كه داراي يك صفت است.) بخش تحتاني حاوي عمليات كلاس است، كه متناظر با توابع عضو در
C++
ميباشد.
شكل 2-3
| دياگرام كلاس
UML
نشان ميدهد كه كلاس
GradeBook
داراي يك عمليات سراسري بنام
displayMessage
است.
UML
مبادرت به مدلسازي عملياتها با ليست كردن نام عمليات و بدنبال آن مجموعه پرانتزها ميكند. كلاس
GradeBook
فقط داراي يك تابع عضو بنام
displayMessage
است، از اينرو بخش تحتاني در شكل 2-3 فقط يك عمليات با اين نام را ليست كرده است. تابع عضو
displayMessage
براي انجام وظيفه خود نيازي به اطلاعات اضافي ندارد، از اينرو پرانتزهاي قرار گرفته پس از
displayMessage
در اين دياگرام كلاس خالي هستند، همانند سرآيند تابع عضو قرار گرفته در خط
13
برنامه1-3.
علامت جمع (
+
) كه قبل ا
ز نام عمليات آورده شده، نشان ميدهد كه
displayMessage
يك عمليات سراسري در
UML
است (يك تابع عضو
public
در
C++
). به دفعات از دياگرامهاي كلاس
UML
براي خلاصه كردن صفات و عمليات كلاسها استفاده خواهيم كرد.
5-3 تعريف تابع عضو با پارامتر
در مثال قياس اتومبيل در بخش 2-3 خاطرنشان كرديم كه فشردن پدال گاز سبب ارسال پيغامي به اتومبيل ميشود تا وظيفهاي را به انجام برساند، كه در اين مورد افزودن سرعت اتومبيل است. اما اتومبيل بايد چقدر سرعت بگيرد؟ همانطوري كه ميدانيد، با فشردن هر چه بيشتر پدال، سرعت اتومبيل افزايش پيدا ميكند. بنابر اين پيغام ارسالي به اتومبيل هم حاوي وظيفه بوده و همچنين حاوي اطلاعات ديگري است كه به اتومبيل در انجام وظيفه كمك ميكند. اين اطلاعات اضافي بنام پارامتر شناخته ميشوند، مقدار پارامتر به اتومبيل كمك ميكند تا ميزان افزايش سرعت را تعيين كند. به همين ترتيب، يك تابع عضو ميتواند نيازمند يك يا چندين پارامتر بعنوان داده اضافي براي انجام وظيفه خود باشد. فراخوان تابع مقاديري بنام آرگومان، براي هر پارامتر تابع تدارك ميبيند. براي مثال، در سپردهگذاري در حساب بانكي، فرض كنيد تابع عضو
deposit
از كلاس
Account
پارامتري كه نشاندهنده مقدار سپرده است، مشخص كرده باشد. زمانيكه تابع عضو
deposit
فراخواني شود، مقدار آرگومان كه نشاندهنده مقدار سپرده است به پارامتر تابع عضو كپي ميشود. سپس تابع عضو اين مقدار سپرده را به ميزان موجودي اضافه ميكند.
تعريف و تست كلاس
GradeBook
مثال بع
دي (برنامه شكل 3-3) تعريف مجددي از كلاس
GradeBook
(خطوط
14-23
) با تابع عضو
displayMessage
است (خطوط
18-22
) كه نام دوره را بعنوان بخشي از رشته خوشآمدگويي چاپ ميكند. تابع عضو جديد
displayMessage
مستلزم يك پارامتر است (
courseName
در خط
18
) كه نشاندهنده نام دوره است.
قبل از اينكه به بررسي ويژگيهاي جديد كلاس
GradeBook
بپردازيم، اجازه دهيد به نحوه استفاده از كلاس جديد در
main
نگاهي داشته باشيم
(خ
طوط
26-40
). در خط
28
يك متغير از نوع رشته
(string)
بنام
nameOfCourse
ايجاد شده است كه از آن براي ذخيره كردن نام دوره وارد شده توسط كاربر استفاده خواهيم كرد. متغير از نوع
string
نشاندهنده رشتهاي از كاراكترها همانند
"CS101 Introduction to C++ Programming"
است. نوع
string
در واقع شي از كلاس
string
كتابخانه استاندارد
C++
است. اين كلاس در فايل سرآيند
<string>
تعريف شده و نام
string
همانند
cout
، متعلق به فضاينامي
std
است. براي اينكه خط
28
قادر به كامپايل شدن باشد، خط
9
حاوي سرآيند
<string>
است. دقت كنيد كه اعلان
using
در خط
10
به ما اجازه ميدهد تا به آساني در خط
28
از
string
بجاي
std::string
استفاده كنيم. براي اين لحظه، ميتوانيد در مورد متغيرهاي
string
همانند متغيرهاي از نوع ديگر همانند
int
فكر كنيد. در بخش 10-3 با قابليتهاي
string
بيشتر آشنا خواهيد شد.
خط
29
يك شي از كلاس
GradeBook
بنام
myGradeBook
ايجاد ميكند. خط
32
به كاربر اعلان ميكند كه نام دوره را وارد سازد. خط
33
مبادرت به خواندن نام دوره از ورودي كاربر كرده و آن را با استفاده از تابع كتابخانهاي
getline
به متغير
nameOfCourse
تخصيص ميدهد. قبل از اينكه به توضيح اين خط از كد بپردازيم، اجازه دهيد تا توضيح دهيم چرا نميتوانيم براي بدست آوردن نام دوره فقط بنويسيم
cin>>nameOfCourse;
در اجراي نمونه اين برنامه، ما از نام دوره
"CS101 Introduction to C++ Programming"
استفاده كردهايم كه حاوي چندين كلمه است. زمانيكه
cin
با عملگر استخراج به كار گرفته ميشود، مبادرت به خواندن كاراكترها تا رسيدن به اولين كاراكتر
white-space
ميكند. از اينرو فقط
"CS101"
توسط اين عبارت خوانده ميشود و براي خواندن مابقي نام دوره مجبور به عمليات ورودي اضافي خواهيم بود.
در اين مثال، مايل هستيم تا كاربر نام كامل دوره را تايپ كرده و كليد
Enter
را براي تحويل آن به برنامه فشار دهد، و كل نام دوره در متغير رشتهاي
nameOfCourse
ذخيره گردد فراخواني تابع
getline(
cin, nameOfCourse)
در خط
33
، سبب ميشود كه كاراكترها (از جمله كاراكترهاي فاصله كه كلمات را از هم در ورودي جدا ميكنند) از شي ورودي استاندارد جريان
cin
(صفحه كليد) تا رسيدن به كاراكتر خط جديد خوانده شده و در متغير رشتهاي
nameOfCourse
جاي داده شده و كاراكتر خط جديد حذف ميگردد. توجه نمائيد زمانيكه كليد
Enter
فشرده ميشود (در زمان تايپ ورودي)، يك خط جديد
(newline)
به جريان ورودي افزوده ميشود. همچنين توجه كنيد كه فايل سرآيند
<string>
بايد قرار داشته باشد تا بتوان از تابع
getline
استفاده كرد. نام اين تابع متعلق به فضاي نامي
std
است.
1
// Fig. 3.3: fig03_03.cpp
2
// Define class GradeBook with a member function that takes a parameter;
3
// Create a GradeBook object and call its displayMessage function.
4
#include <iostream>
5
using std::cout;
6
using std::cin;
7
using std::endl;
8
9
#include <string> // program uses C++ standard string class
10
using std::string;
11
using std::getline;
12
13
// GradeBook class definition
14
class GradeBook
15
{
16
public:
17
// function that displays a welcome message to the GradeBook user
18
void displayMessage( string courseName )
19
{
20
cout << "Welcome to the grade book for\n" << courseName << "!"
21
<< endl;
22
} // end function displayMessage
23
}; // end class GradeBook
24
25
// function main begins program execution
26
int main()
27
{
28
string nameOfCourse; // string of characters to store the course name
29
GradeBook myGradeBook; // create a GradeBook object named myGradeBook
30
31
// prompt for and input course name
32
cout << "Please enter the course name:" << endl;
33
getline( cin, nameOfCourse ); // read a course name with blanks
34
cout << endl; // output a blank line
35
36
// call myGradeBook's displayMessage function
37
// and pass nameOfCourse as an argument
38
myGradeBook.displayMessage( nameOfCourse );
39
return 0; // indicate successful termination
40
} // end main
Please enter the course name:
CS101 Introduction to C++ Programming
Welcome to the grade book for
CS101 Introduction to C++ Programming!
|
شكل 3-3 | تعريف كلاس
GradeBook
با تابع عضوي كه پارامتر دريافت ميكند.
خط
38
تابع عضو
displayMessage
شي
myGradeBook
را فراخواني ميكند. متغير
nameOfCource
موجود در درون پرانتزها آرگوماني است كه به تابع
displayMessage
ارسال ميشود، از اينروست كه تابع ميتواند وظيفه خود را به انجام رساند. مقدار متغير
nameOfCourse
در
main
تبديل به مقدار پارامتر
courseName
تابع عضو
displayMessage
در خط
18
ميشود. زمانيكه اين برنامه اجرا شود، توجه نمائيد كه تابع عضو
displayMessage
به همراه پيغام خوشآمدگويي، نام دورة تايپ شده توسط كاربر را به نمايش درميآورد.
آرگومانها و پارامترهاي بيشتر
براي تصريح اينكه تابعي ني
ازمند داده براي انجام وظايف خويش است، اطلاعات اضافي را در ليست پارامتري تابع قرار ميدهيم، ليستي كه در درون پرانتزها قرار گرفته پس از نام تابع جاي دارد. ليست پارامترها ميتواند به هر تعداد پارامتر داشته باشد يا اصلاً پارامتري نداشته باشد (همانند پرانتزهاي خالي در خط
13
برنامه شكل 1-3) تا نشان دهد تابع نياز به هيچ پارامتري ندارد. ليست پارامتري تابع عضو
displayMessage
به نحوي اعلان شده كه تابع نيازمند يك پارامتر است (شكل 3-3، خط
18
). هر پارامتر بايد داراي نوع و يك شناسه باشد. در اين مورد، نوع
string
بوده و شناسه
courseName
است و نشان ميدهد كه تابع عضو
displayMessage
مستلزم يك رشته براي انجام وظيفه خويش است. بدنه تابع عضو از پارامتر
courseName
براي دسترسي به مقدار ارسالي به تابع در فراخواني تابع (خط
38
در
main
) استفاده ميكند. خطوط
20-21
مقدار پارامتر
courseName
را بعنوان بخشي از پيغام خوشآمدگويي به نمايش در ميآورد. توجه نمائيد كه نام متغير پارامتري (خط
18
) ميتواند همنام متغير آرگومان (خط
38
) يا نام متفاوتي داشته باشد. در فصل ششم با دلايل اينكارها آشنا خواهيد شد.
يك تابع ميتواند داراي چندين پارامتر باشد، بشرطي كه هر پارامتر از ديگري با يك ويرگول مجزا شده باشد (در مثال شكلهاي 4-6 و 5-6 شاهد آنها خواهيد بود). تعداد و ترتيب آرگومانها در يك تابع فراخواني شده بايستي با تعداد و ترتيب ليست پارامترها در سرآيند تابع عضو فراخواني شده مطابقت داشته باشد. همچنين، نوع آرگومانها در تابع فراخواني شده بايد با نوع پارامترهاي متناظر در سرآيند تابع مطابقت داشته باشد. (همانطوري كه جلوتر ميرويم، شاهد خواهيد بود كه نوع يك آرگومان ميتواند همانند نوع پارامتر متناظر خود نباشد.) در مثال ما، يك آرگومان رشتهاي در تابع فراخواني شده وجود دارد
(nameOfCourse)
كه دقيقاً با پارامتري رشتهاي در تعريف تابع عضو مطابقت دارد
(CourseName)
.
خطاي برنامهنويسي
قراردادن يك سيمكولن پس از پرانتز سمت راست در ليست پارامتري تعريف تابع، خطاي نحوي است.
خطاي برنامهنويسي
تعريف يك پارامتر تابع همانند يك متغير محلي در تابع، خطاي كامپايل بدنبال خواهد داشت.
برنامهنويسي ايدهال
از ابهام اجتناب كنيد، از اسامي يكسان براي آرگومانهاي ارسالي به تابع و پارامترهاي متناظر در تعريف تابع، استفاده نكنيد.
برنامهنويسي ايدهال
براي توابع و پارامترها اسامي با معني انتخاب كنيد تا خوانايي برنامه افزايش يافته و نياز به افزودن توضيحات را كم نمايد.
به روز كردن دياگرام كلاس
UML
براي كلاس
GradeBook
دياگرام كلاس
UML
در شكل 4-3 مبادرت به مدلسازي كلاس
GradeBook
از برنامه 3-3 كرده است. همانند كلاس
GradeBook
تعريف شده در برنامه 1-3، اين كلاس
GradeBook
حاوي يك تابع عضو سراسري
displayMessage
است. با اين وجود، اين نسخه از
displayMessage
داراي يك پارامتر ميباشد.
UML
پارامتر را با ليست كردن نام پارامتر، بدنبال آن يك كولن و سپس نوع پارامتر در درون پرانتزهاي قرار گرفته پس از نام عمليات مدلسازي ميكند.
UML
داراي نوع دادههاي متعلق بخود شبيه
C++
است.
UML
يك زبان مستقل بوده و از آن در كنار انواع زبانهاي برنامهنويسي استفاده ميشود، از اينرو اصطلاحات تخصصي آن دقيقاً مطابق با
C++
نيست. براي مثال، نوع رشته
(String)
در
UML
متناظر با نوع
string
در
C++
است. تابع عضو
displayMessage
از كلاس
GradeBook
(شكل 3-3 خطوط
18-22
) داراي يك پارامتر رشتهاي بنام
CourseName
است، از اينرو در شكل 4-3 بصورت
courseName
: String
در ميان پرانتزها و پس از نام
diaplayName
، قرار گرفته است. دقت كنيد كه اين نسخه از كلاس
GradeBook
هنوز داراي اعضاي داده نيست.
شكل 4-3 | دياگرام كلاس
UML
نشان ميدهد كه كلاس
GradeBook
داراي عمليات
displayMessage
با يك پارامتر از نوع
String
در
UML
است.
6-3 اعضاي داده، توابع
set
و
get
در فصل دوم، تمام متغيرهاي برنامه را در تابع
main
اعلان كرديم. متغيرهاي اعلان شده در بدنه تعريف يك تابع، بنام متغيرهاي محلي شناخته ميشوند و ميتوانند فقط از خطي كه در آن تابع اعلان شدهاند تا رسيدن به براكت بستن سمت راست (
}
) در تعريف تابع بكار گرفته شوند. قبل از اينكه بتوان از يك متغير محلي استفاده كرد، بايد ابتدا آن را اعلان كرده باشيد. زمانيكه تابع به كار خود خاتمه ميدهد، مقادير متغيرهاي محلي آن از بين ميروند (در فصل ششم با متغيرهاي محلي
static
آشنا خواهيد شد كه از اين قاعده مستثني هستند). از بخش 2-3 بخاطر داريد كه شيي كه داراي صفت است، آن را با خود همراه دارد و در برنامه از آن استفاده ميشود. چنين صفاتي در كل طول عمر يك شي وجود خواهند داشت.
معمولاً كلاسي كه داراي يك يا چندين تابع عضو است، مبادرت به دستكاري كردن صفات متعلق به يك شي مشخص از كلاسي ميكند. صفات به عنوان متغيرهاي در تعريف كلاس عرضه ميشوند. چنين متغيرهاي، بنام اعضاي داده شناخته ميشوند و در درون تعريف كلاس اعلان ميگردند اما در خارج از تعريف بدنه تابع عضو كلاس جاي ميگيرند. هر شي از كلاس مسئول نگهداري كپي از صفات متعلق به خود در حافظه است. مثال مطرح شده در اين بخش به توصيف كلاس
GradeBook
ميپردازد كه حاوي يك عضو داده
CourseName
است تا نشاندهنده نام يك دوره خاص از شي
GradeBook
باشد.
كلاس
GradeBook
با يك عضو داده، يك تابع
set
و
get
در اين مثال، كلاس
GradeBook
(برنامه شكل 5-3) مبادرت به نگهداري نام دوره بصورت يك عضو داده ميكند و از اينروست كه ميتواند بكار گرفته شده يا در هر زمان اجراي برنامه آن را اصلاح كرد. كلاس حاوي توابع عضو
setCourseName
،
getCourseName
و
displayMessage
است. تابع عضو
setCourseName
نام دوره را در عضو داده
GradeBook
ذخيره ميسازد. تابع عضو
getCourseName
نام دوره را از عضو داده بدست ميآورد. تابع عضو
displayMessage
كه هيچ پارامتري ندارد، پيغام خوشآمدگويي را كه شامل نام دوره است، به نمايش در ميآورد. با اين همه، همانطوري كه مشاهده ميكنيد، در حال حاضر تابع، نام دوره را با فراخواني تابع ديگري در همان كلاس بدست ميآورد،
تابع
getCourseName
.
1
// Fig. 3.5: fig03_05.cpp
2
// Define class GradeBook that contains a courseName data member
3
// and member functions to set and get its value;
4
// Create and manipulate a GradeBook object.
5
#include <iostream>
6
using std::cout;
7
using std::cin;
8
using std::endl;
9
10
#include <string> // program uses C++ standard string class
11
using std::string;
12
using std::getline;
13
14
// GradeBook class definition
15
class GradeBook
16
{
17
public:
18
// function that sets the course name
19
void setCourseName( string name )
20
{
21
courseName = name; // store the course name in the object
22
} // end function setCourseName
23
24
// function that gets the course name
25
string getCourseName()
26
{
27
return courseName; // return the object's courseName
28
} // end function getCourseName
29
30
// function that displays a welcome message
31
void displayMessage()
32
{
33
// this statement calls getCourseName to get the
34
// name of the course this GradeBook represents
35
cout << "Welcome to the grade book for\n" << getCourseName() << "!"
36
<< endl;
37
} // end function displayMessage
38
private:
39
string courseName; // course name for this GradeBook
40
}; // end class GradeBook
41
42
// function main begins program execution
43
int main()
44
{
45
string nameOfCourse; // string of characters to store the course name
46
GradeBook myGradeBook; // create a GradeBook object named myGradeBook
47
48
// display initial value of courseName
49
cout << "Initial course name is: " << myGradeBook.getCourseName()
50
<< endl;
51
52
// prompt for, input and set course name
53
cout << "\nPlease enter the course name:" << endl;
54
getline( cin, nameOfCourse ); // read a course name with blanks
55
myGradeBook.setCourseName( nameOfCourse ); // set the course name
56
57
cout << endl; // outputs a blank line
58
myGradeBook.displayMessage(); // display message with new course name
59
return 0; // indicate successful termination
60
} // end main
Initial course name is:
Please enter the course name:
CS101 Introduction to C++ Programming
Welcome to the grade book for
CS101 Introduction to C++ Programming!
|
شكل 5-3 | تعريف و تست كلاس
GradeBook
با يك عضو داده و توابع
set
و
get
.
برنامهنويسي ايدهال
يك خط خالي مابين تعريف تابع عضو قرار دهيد تا خوانايي برنامه افزايش پيدا كند.
معمولاً يك مدرس بيش از يك دوره را تدريس ميكند كه هر دوره داراي نام متعلق بخود است. در خط
39
، متغير
CourseName
از نوع رشته اعلان شده است. بدليل اينكه متغير اعلان شده در درون تعريف كلاس اعلان شده (خطوط
15-40
) اما در خارج از بدنه تعاريف توابع عضو جاي دارد (خطوط
19-22
،
25-28
و
31-37
)، خط
39
اعلاني براي يك عضو داده است. هر نمونهاي (شي) از كلاس
GradeBook
حاوي يك كپي از هر عضو داده كلاس خواهد بود. براي مثال، اگر دو شي
GradeBook
وجود داشته باشد، هر شي داراي يك كپي از
CourseName
متعلق ب
خ
ود خواهد بود، همانطوري كه در مثال برنامه شكل 7-3 شاهد آن خواهيد بود. مزيت ايجاد
CourseName
بصورت يك عضو داده در اين است كه تمام توابع عضو يك كلاس (در اين مورد،
GradBook
) ميتوانند در هر عضو داده موجود كه در تعريف كلاس وجود دارد، دستكاري كنند (در اين مورد،
CourseName
).