0

آموزش ++C - بخش دهم

 
reza1371
reza1371
کاربر برنزی
تاریخ عضویت : مهر 1388 
تعداد پست ها : 168
محل سکونت : لرستان

آموزش ++C - بخش دهم

قسمت قبل را با معرفي انواع بلوك‌هاي‌ سازنده كه در حل مسئله نقش دارند، آغاز كرديم. با استفاده از اين بلوك‌هاي سازنده تكنيك‌هاي ايجاد برنامه را بهبود بخشيديم. در اين فصل، مبحث تئوري و قواعد علمي برنامه‌نويسي ساخت‌يافته را با معرفي مابقي عبارات كنترلي C++ ادامه مي‌دهيم. با عبارات كنترلي كه در اين فصل مطرح مي‌كنيم و عبارات كنترلي معرفي شده در فصل قبلي قادر به ايجاد و كنترل شي‌ها خواهيم بود. همچنين به مبحث برنامه‌نويسي شي‌گرا كه آن را از فصل اول آغاز كرده‌ايم ادامه مي‌دهيم.

در اين قسمت به توصيف عبارات do...while, for و switch مي‌پردازيم. در كنار مثال‌هاي كوچكي كه در آنها از while و for استفاده شده، به بررسي اصول و نيازهاي شمارنده-كنترل‌تكرار خواهيم پرداخت. بخشي از اين فصل را اختصاص به گسترش كلاس GradeBook عرضه شده در فصل‌هاي سوم و چهارم داده‌ايم. در واقع، نسخه‌اي از كلاس GradeBook را ايجاد مي‌كنيم كه از عبارت switch براي شمارش تعداد نمرات A ، B ، C ، D و F وارد شده از سوي كاربر استفاده مي‌كند. به معرفي عبارات break و continue خواهيم پرداخت كه كنترل‌كننده برنامه هستند. در مورد عملگرهاي منطقي، كه به برنامه‌نويسان اجازه مي‌دهند تا از شرط‌هاي پيچيده و قدرتمندتر در عبارات كنترلي استفاده كنند، صحبت خواهيم كرد.

همچنين در ارتباط با خطاي رايجي كه در ارتباط با عدم درك صحيح تفاوت مابين عملگر برابري ( == ) و تخصيص ( = ) رخ مي‌دهد توضيحاتي ارائه مي‌كنيم. در پايان، بطور خلاصه شده به توصيف عبارات كنترلي C++ و تكنيك‌هاي حل مسئله مطرح شده در اين فصل و فصل چهارم مي‌پردازيم.

2-5  نكاتي در مورد شمارنده-كنترل تكرار

در فصل قبل، با مفهوم روش شمارنده-كنترل تكرار آشنا شديد. در اين بخش با استفاده از عبارت تكرار while ، اقدام به فرموله كردن عناصر مورد نياز در روش شمارنده-كنترل تكرار مي‌كنيم:

1- نام متغير كنترل (يا شمارنده حلقه) كه براي تعيين تكرار حلقه بكار گرفته مي‌شود.

2- مقداراوليه متغير كنترل.

3- شرط تكرار حلقه براي تست مقدار نهايي متغير كنترل (آيا حلقه ادامه يابد يا خير).

4- افزايش (يا كاهش) متغير كنترل در هر بار تكرار حلقه.

در برنامه شكل 1-5 از چهار عنصر شمارنده-كنترل تكرار براي نمايش ارقام 10-1 استفاده شده است. در خط 9 ، نام متغير كنترلي (counter) از نوع صحيح اعلان شده و فضاي در حافظه براي آن رزرو مي‌شود و با مقدار اوليه 1 تنظيم شده است. اين اعلان يك مقداردهي اوليه است. بخش مقداردهي اين عبارت يك جزء اجرائي است و از اينرو، خود عبارت هم اجرائي مي‌باشد.

اعلان و مقداردهي اوليه counter در خط 9 را مي‌توان توسط عبارات زير هم انجام داد:

int counter; // declare control variable

counter = 1; // initialize control variable to 1

ما از هر دو روش استفاده مي‌كنيم.

به عبارت while (خطوط 11-15 ) توجه كنيد. خط 14 مقدار جاري counter را به ميزان 1 واحد در هر بار تكرار حلقه افزايش مي‌دهد. شرط تكرار حلقه (خط 11 ) در عبارت while تست مي‌كند كه آيا مقدار متغير كنترل كمتر يا برابر 10 است يا خير، به اين معني كه 10 ، مقدار نهايي براي برقرار بودن شرط است. بدنه عبارت while تا زمانيكه متغير كنترل به 5 برسد اجرا مي‌شود. زمانيكه مقدار متغير كنترل بيش از 10 شود (هنگامي كه counter به مقدار 11 برسد)، حلقه پايان مي‌يابد.

1    // Fig. 5.1: fig05_01.cpp

2    // Counter-controlled repetition.

3    #include <iostream>

4    using std::cout;

5    using std::endl;

6     

7    int main()

8    {

9       int counter = 1; // declare and initialize control variable

10   

11     while ( counter <= 10 ) // loop-continuation condition

12     {   

13        cout << counter << " ";

14        counter++; // increment control variable by 1

15     } // end while

16   

17     cout << endl; // output a newline

18     return 0; // successful termination

19  } // end main

شكل 1-5 | شمارنده-كنترل تكرار .

برنامه‌نويسي ايده‌ال

قبل و بعد از هر عبارت يك خط خالي قرار دهيد تا قسمت‌هاي متفاوت برنامه از يكديگر تميز داده شوند.

 

 

برنامه‌نويسي ايده‌ال

قراردادن فضاهاي خالي در ابتدا و انتهاي عبارتهاي كنترلي و دندانه‌دار كردن اين عبارتها به برنامه يك ظاهر                   دوبعدي مي‌دهد و باعث افزايش خوانايي برنامه مي‌شود.

با مقداردهي counter با 0 و جايگزين كردن عبارت while با

while( ++counter <= 10 ) //loop-continuation condition

               cout << counter << " ";

مي‌توان برنامه شكل 1-5 را مختصرتر كرد. با اين كد از يك عبارت صرفه‌جويي شده است، چرا كه عمليات افزايش بصورت مستقيم در شرط while قبل از بررسي شرط صورت مي‌گيرد. همچنين اين كد نياز به استفاده از براكت‌ها در اطراف بدنه while را از بين برده است، چرا كه while فقط حاوي يك عبارت است. گاهي اوقات فشرده‌سازي كد به اين روش مي‌تواند خوانايي، نگهداري، اصلاح و خطايابي برنامه را مشكل كند.

 

برنامه‌نويسي ايده‌ال

با افزايش تعداد سطح‌هاي تودرتو، درك عملكرد برنامه مشكل‌تر مي‌شود. بعنوان يك قانون، سعي كنيد از سه                  سطح تودرتو فراتر نرويد.

 

خطاي برنامه‌نويسي

مقادير اعشاري تقريبي هستند، از اينرو كنترل شمارش حلقه‌ها با متغيرهاي اعشاري مي‌تواند در شمارش                           مقادير، غيردقيق بوده و شرط خاتمه را بدقت انجام ندهند.

 

 

اجتناب از خطا

شمارش حلقه‌ها را با مقادير صحيح كنترل كنيد.

3-5  عبارت تكرار for

در بخش 2-5 به بررسي اصول شمارنده-كنترل تكرار پرداخته شد. مي‌توان از عبارت while در پياده‌سازي هر حلقه كنترل شده با شمارنده استفاده كرد. زبان C++ داراي عبارت تكرار for است كه از تمام جزئيات شمارنده-كنترل‌تكرار استفاده مي‌كند. براي نشان دادن قدرت for برنامه 1-5 را مجدداً بازنويسي مي‌كنيم. نتيجه اينكار در برنامه شكل 2-5 ديده مي‌شود.

1    // Fig. 5.2: fig05_02.cpp

2    // Counter-controlled repetition with the for statement.

3    #include <iostream>

4    using std::cout;

5    using std::endl;

6     

7    int main()

8    {

9       // for statement header includes initialization,

10     // loop-continuation condition and increment.

11     for ( int counter = 1; counter <= 10; counter++ )

12        cout << counter << " ";

13   

14     cout << endl; // output a newline

15     return 0; // indicate successful termination

16  } // end main

شكل 2-5 | شمارنده-كنترل‌تكرار با عبارت for .

هنگامي كه عبارت for شروع بكار مي‌كند (خطوط 11-12 ) ، متغير كنترل counter با 1 مقداردهي مي‌شود، از اينرو تا بدين جا دو عنصر اوليه شمارنده-كنترل تكرار يعني نام متغير و مقدار اوليه تعيين شده‌اند. سپس، شرط ادامه حلقه counter <= 10 بررسي مي‌شود. بدليل اينكه مقدار اوليه متغير counter ، برابر 1 است، شرط برقرار بوده، و مقدار 1 با اجراي عبارت خط 12 چاپ مي‌شود. سپس مقدار متغير counter توسط عبارت counter++ افزايش يافته و مجددا حلقه با تست شرط تكرار آغاز مي‌شود. در اين لحظه، متغير كنترل حاوي مقدار 2 است. اين مقدار متجاوز از مقدار پاياني نيست و از اينرو برنامه عبارت موجود در بدنه را به اجرا در مي‌آورد. اين فرآيند تا زمانيكه counter به مقدار 10 برسد و آنرا چاپ كند و متغير كنترل counter به 11 افزايش يابد، ادامه پيدا مي‌كند و موجب مي‌شود تا تست شرط حلقه برقرار نشده و تكرار خاتمه يابد. برنامه با اجراي اولين عبارت پس از عبارت for ادامه مي‌يابد (در اين مثال، برنامه به عبارت خروجي  در خط 14 مي‌رسد و آنرا اجرا مي‌كند).

كامپونت‌هاي تشكيل دهنده سرآيند for

در شكل3-5 نگاهي دقيق‌تر بر عبارت for برنامه 2-5 داشته‌ايم (خط 11 ) . گاهاً به خط اول عبارت for سرآيند for مي‌گويند. اين سرآيند حاوي ايتم‌هاي مورد نياز در ايجاد شمارنده-كنترل‌تكرار به همراه يك متغير كنترلي است.

دقت كنيد كه در برنامه2-5 از شرط تكرار حلقه counter <= 10 استفاده شده است. اگر برنامه‌نويس اشتباهاً بنويسد counter < 10 ، حلقه فقط 9 بار اجرا خواهد شد. اين خطا معروف به خطاي off-by-one است.

 

 

خطاي برنامه‌نويسي

استفاده از عملگر رابطه‌اي اشتباه يا استفاده از يك مقدار نهايي اشتباه در شرط شمارنده يك حلقه while يا for مي‌تواند خطاي off-by-one بدنبال داشته باشد.

 

 

برنامه‌نويسي ايده‌ال

استفاده از مقدار نهايي در شرط يك عبارت while يا for و استفاده از عملگر رابطه‌اي <= مي‌تواند جلوي خطاهاي off-by-one را بگيريد. براي مثال، در حلقه‌اي كه براي چاپ مقادير 1 تا 10 كاربرد دارد، شرط تكرار حلقه بايستي counter <= 10 بجاي counter < 10 يا counter < 11 باشد. برخي از برنامه‌نويسان ترجيح مي‌دهند شمارش را از صفر آغاز كنند، در اينحالت براي شمارش 10 بار تكرار حلقه، بايستي متغير counter با صفر مقدارهي شده و شرط تست حلقه به صورت counter < 10 نوشته شود.

فرمت كلي عبارت for بشكل زير است

for ( مقداردهي اوليه ; شرط تكرار حلقه ; افزايش )

       عبارت

در اين عبارت، جمله مقداردهي اوليه نشاندهنده نام متغير كنترلي حلقه بوده و مقدار اوليه را فراهم مي‌آورد، شرط تكرار حلقه حاوي مقدار پاياني متغير كنترلي بوده و در صورت برقرار بودن حلقه به اجرا در مي‌آيد، و جمله افزايش مبادرت به افزودن مقدار متغير كنترلي مي‌كند. مي‌توان بجاي عبارت for از معادل عبارت while و بصورت زير استفاده كرد:

مقداردهي اوليه ;

while ( شرط تكرار حلقه ) {

      عبارت

     افزايش ;  

}

در اينجا فقط يك استثناء وجود دارد كه در بخش 7-5 به بررسي آن خواهيم پرداخت.

اگر جمله مقداردهي اوليه در سرآيند عبارت for اعلان‌كننده متغير‌كنترلي باشد (نوع متغيركنترلي قبل از نام متغير آمده باشد)، مي‌توان از آن متغير كنترلي فقط در بدنه همان for استفاده كرد. اين متغير كنترلي در خارج از عبارت for شناخته شده نخواهد بود. اين محدوديت استفاده از نام متغير كنترلي بعنوان قلمرو متغير شناخته مي‌شود. قلمرو يك متغير تعيين‌كننده‌ مكاني است كه متغير مي‌تواند در برنامه بكار گرفته شود. در فصل ششم با جزئيات قلمرو آشنا خواهيد شد.

 

 

خطاي برنامه‌نويسي

زمانيكه متغير كنترلي در يك عبارت for در بخش مقداردهي اوليه سرآيند for اعلان شده باشد، استفاده از آن متغير كنترلي پس از عبارت، يك خطاي كامپايل بدنبال خواهد داشت.

 

 

قابليت حمل

در C++ استاندارد، قلمرو متغير كنترلي اعلان شده در بخش مقداردهي اوليه يك عبارت for با قلمرو مطرح شده در كامپايلرهاي قديمي C++ متفاوت است . در كامپايلرهاي قديمي، قلمرو متغير كنترلي با پايان يافتن بلوك تعيين ‌كننده عبارت for خاتمه‌ نمي‌پذيرد. كد C++ ايجاد شده با كامپايلرهاي قديمي C++ مي‌تواند به هنگام كامپايل با كامپايلرهاي استاندارد با مشكل مواجه شود. اگر در حال كار با كامپايلرهاي قديمي هستيد و مي‌خواهيد از عملكرد دقيق كدهاي خود بر روي كامپايلرهاي استاندارد مطمئن شويد، دو استراتژي برنامه‌نويسي تدافعي وجود دارد كه مي‌توانيد از آنها استفاده كنيد: اعلان متغيرهاي كنترلي با اسامي مختلف در هر عبارت for   يا اگر ترجيح مي‌دهيد از نام يكساني براي متغير كنترلي در چندين عبارت for   استفاده كنيد، اعلان متغير كنترلي قبل از اولين عبارت for است.

همانطوري كه مشاهده خواهيد كرد، جملات مقداردهي اوليه و افزايش را مي‌توان با كاما از يكديگر متمايز كرد. كاماي (ويرگول) بكار رفته در اين جملات، از نوع عملگرهاي كاما هستند و تضمين مي‌كنند كه ليست جملات از سمت چپ به راست ارزيابي خواهند شد. عملگر كاما از تمام عملگرهاي C++ از تقدم پايين‌تري برخوردار است. مقدار و نوع يك ليست جدا شده با كاما، معادل مقدار و نوع سمت راست‌ترين جمله در ليست خواهد بود. در اكثر مواقع از كاما در عبارات for استفاده مي‌شود. اصلي‌ترين كاربرد كاما اين است كه برنامه‌نويس امكان استفاده از چندين جمله مقداردهي اوليه و يا چندين جمله افزايش‌دهنده را فراهم مي‌آورد. براي مثال، امكان دارد چندين متغير كنترلي در يك عبارت for  وجود داشته باشند كه بايستي مقداردهي اوليه شده و افزايش داده شوند.

 

مهندسي نرم‌افزار

با قرار دادن يك سيمكولن بلافاصله پس از سرآيند for ،يك حلقه تاخيردهنده بوجود مي‌آيد. چنين حلقه‌اي با يك بدنه تهي فقط به تعداد دفعات مشخص شده تكرار شده و كاري بجز شمارش انجام نمي‌دهد. براي مثال، امكان دارد از يك حلقه تاخيري براي كاستن از سرعت قراردادن خروجي در صفحه نمايش استفاده كنيد تا كاربر بتواند براحتي آن را بخواند. اما بايد مراقب باشيد، چرا كه چنين زمان تاخيري در ميان انواع سيستم‌ها با پردازنده‌هاي مختلف متفاوت است.

سه عبارت در عبارت for حالت اختياري دارند. اگر شرط تكرار حلقه فراموش شود، C++ فرض خواهد كرد كه شرط تكرار حلقه برقرار است، و از اينرو يك حلقه بي‌نهايت بوجود خواهد آمد. مي‌توان عبارت مقداردهي اوليه متغير كنترلي را از عبارت for خارج كرد، اگر اين متغير در جاي ديگري از برنامه و قبل از حلقه مقداردهي شده باشد. عبارت سوم مربوط به بخش افزايش است و در صورتيكه عمليات افزايش مقدار متغير كنترلي توسط عبارتي در بدنه for صورت گيرد يا نيازي به افزايش وجود نداشته باشد، مي‌توان آنرا از سرآيند حذف كرد. عبارت افزايشي در عبارت for همانند يك عبارت منفرد در انتهاي بدنه for عمل مي‌كند. از اينرو، عبارات

counter = counter + 1

counter += 1

++counter

counter++

همگي معادل بخش افزايشي در سرآيند عبارت for هستند. بسياري از برنامه‌نويسان ترجيح مي‌دهند تا از counter++ استفاده كنند. به اين دليل كه عمليات افزايش مقدار متغير كنترلي را پس از اجراي بدنه حلقه به انجام مي‌رساند، و از اينرو اين رفتار در افزايش بسيار طبيعي بنظر مي‌رسد.

 

خطاي برنامه‌نويسي                                  

استفاده از كاما بجاي سيمكولن در سرآيند عبارت for  خطاي نحوي خواهد بود.

 

خطاي برنامه‌نويسي                                   

قرار دادن يك سيمكولن بلافاصله در سمت راست پرانتز يك سرآيند for يك بدنه تهي براي for توليد خواهد كرد. اينكار مي‌تواند خطاي منطقي بدنبال داشته باشد .

بخش‌هاي مقداردهي اوليه، شرط تكرار حلقه و افزايش در عبارت for مي‌توانند حاوي عبارات محاسباتي باشند. براي مثال، فرض كنيد كه x = 2 و y = 10 باشد. اگر x و y در داخل بدنه حلقه تغييري نيابد، پس عبارت

for ( int j = x; j <= 4 * x * y; j += y / x)

معادل عبارت زير خواهد بود

for ( int j = 2; j <= 80; j += 5)

مقدار بخش افزايش مي‌تواند منفي باشد، در اينحالت گام پيمايش حلقه معكوس خواهد بود. اگر شرط تكرار حلقه در همان ابتداي كار برقرار نباشد، بدنه عبارت for اجرا نخواهد شد، و اجرا با عبارت پس از عبارت for ادامه مي‌يابد.  مقدار متغير كنترلي به دفعات در محاسبات درون بدنه عبارت for بكار گرفته مي‌شود يا به نمايش در مي‌آيد، اما اينكار الزامي ندارد. در بسياري از موارد از متغير كنترلي فقط براي كنترل تكرار استفاده مي‌شود.

 

اجتناب از خطا

اگر چه مقدار، متغير كنترل ي مي‌تواند در بدنه حلقه for تغيير يابد، اما از انجام چنين كاري اجتناب كنيد چرا كه مي‌تواند برنامه را بسمت خطاهاي ناخواسته‌اي هدايت كند.

دياگرام فعاليت UML عبارت for

دياگرام فعاليت UML عبارت for شبيه به دياگرام فعاليت while است (شكل 6-4). در شكل 4-5 دياگرام فعاليت عبارت for بكار رفته در برنامه 2-5 آورده شده است. در اين دياگرام مشخص است كه فرآيند مقداردهي اوليه فقط يكبار و قبل از ارزيابي تست شرط تكرار حلقه براي بار اول صورت مي‌گيرد و اينكه عمل افزايش در هر بار و پس از اجراي عبارات بدنه انجام مي‌شود. دقت كنيد (در كنار وضعيت اوليه، خطوط انتقال، ادغام، وضعيت پاياني) دياگرام فقط حاوي يك وضعيت عمل و يك تصميم‌گيري است.

شكل 4-5 | دياگرام فعاليت UML عبارت تكرار for در برنامه 2-5.

4-5  مثال‌هاي با استفاده از عبارت for

مثال‌هاي مطرح شده در اين بخش، متدهاي متنوعي از كاربرد متغير كنترلي در يك عبارت for هستند. در هر مورد، سرآيند for نوشته شده است.

a ) متغير كنترلي كه از 1 تا 100 با گام 1 افزايش مي‌يابد.

for ( int i = 1; i <= 100; i++)

b ) متغير كنترلي كه از 100 تا 1 با گام -1 افزايش مي‌يابد (با كاهش 1 ).

for ( int i = 100; i >= 1; i--)

c ) متغير كنترلي كه از 7 تا 77 ، با گام 7 افزايش مي‌يابد.

for ( int i = 7; i <= 77; i += 7)   

d ) متغير كنترلي كه از 20 تا 2 با گام -2 افزايش مي‌يابد.

for ( int i = 20; i >= 2; i -=2)

e ) متغير كنترلي، كه توالي از مقادير 20 ، 17 ، 14 ، 11 ، 8 ، 5 و 2 به خود مي‌گيرد.

for ( var j = 2; j <= 20; j += 3)

f ) متغير كنترلي كه توالي از مقادير 0 ، 11 ، 22 ، 33 ، 44 ، 55 ، 66 ، 77 ، 88 و 99 به خود مي‌گيرد.

for ( var j = 99; j >= 20; j -=11)

 

خطاي برنامه‌نويسي

نتيجه عدم استفاده صحيح از عملگر رابطه‌اي در شرط تكرار حلقه كه بصورت معكوس شمارش مي‌كند (همانند استفاده اشتباه i <= 1 بجاي i >= 1 در شمارش معكوس حلقه به سمت 1 ) معمولاً يك خطاي منطقي است كه نتايج اشتباهي به هنگام اجرا برنامه توليد مي‌كند.

 

 

برنامه: مجموع اعداد زوج از 2 تا 20

دو مثال بعدي برنامه‌هاي ساده‌اي هستند كه به توضيح عبارت for مي‌پردازند. برنامه 5-5 از عبارت for براي بدست آوردن مجموع اعداد زوج 100 تا 2 استفاده مي‌كند. در هر بار تكرار حلقه (خطوط 12-13 ) مقدار جاري متغير كنترل number به متغير total افزوده مي‌شود.

1    // Fig. 5.5: fig05_05.cpp

2    // Summing integers with the for statement.

3    #include <iostream>

4    using std::cout;

5    using std::endl;

6     

7    int main()

8    {

9       int total = 0; // initialize total

10   

11     // total even integers from 2 through 20

12     for ( int number = 2; number <= 20; number += 2 )

13        total += number;

14   

15     cout << "Sum is " << total << endl; // display results

16     return 0; // successful termination

17  } // end main

 

 شكل 5-5 | عبارت for بكار رفته براي محاسبه مجموع اعداد زوج.

به بدنه عبارت for در برنامه 5-5 توجه نمائيد. در واقع مي‌توان اين بدنه را با سمت راسترين بخش سرآيند for و با استفاده از كاما ادغام كرد، بصورت زير:

for ( int number = 2; // initialization

      number <= 20; // loop continuation condition

      total += number, number += 2 ) // total and increment

;// empty body

 

 

برنامه‌نويسي ايده‌ال

گرچه ادغام عبارات بدنه for با بخش سرآيند آن وجود دارد اما از انجام اينكار اجتناب كنيد تا درك برنامه مشكل نشود.

 

 

برنامه‌نويسي ايده‌ال

در صورت امكان، سايز عبارات سرآيند كنترل را محدود كنيد.

برنامه:محاسبه سود

مثال بعدي در ارتباط با محاسبه يك مسئله تركيبي با استفاده از عبارت for است. صورت مسئله به شرح زير است:

شخصي 1000.00 دلار در يك حساب پس انداز با سود 5 % سرمايه‌گذاري كرده است. با فرض اينكه كل سود نيز ذخيره مي‌شود، مقدار پول موجود در حساب را در پايان هر سال در يك مدت 10 ساله محاسبه و چاپ كنيد. براي بدست آوردن اين مقادير، از فرمول زير كمك بگيريد:

a = p(1 + r)n

در اين فرمول

p ميزان سرمايه اوليه

r نرخ سود

n تعداد سال‌ها

a مقدار سرمايه در پايان سال  n ام است.

اين مسئله مستلزم حلقه‌اي است كه دلالت بر انجام محاسبه‌اي براي هر سال در مدت ده سال پول باقيمانده در حساب پس‌انداز است. اين راه حل در برنامه شكل 6-5 آورده شده است.

عبارت for (خطوط 28-35 ) مبادرت به اجراي بدنه خود به ميزان 10 بار مي‌كند. مقدار متغير كنترلي از 1 تا 10 به ميزان يك واحد در هر بار افزايش مي‌يابد. زبان C++ حاوي عملگر توان نيست، از اينرو از تابع كتابخانه‌اي استاندارد pow به همين منظور استفاده كرده‌ايم (خط 31 ). تابع pow(x,y) مبادرت به محاسبه مقدار x به توان y مي‌كند. در اين مثال، عبارت جبري (1+r)n بصورت pow(1.0+rate,year) نوشته شده است، كه متغير rate نشاندهنده r و متغير year نشاندهنده n است. تابع pow دو آرگومان از نوع double دريافت و يك مقدار double برگشت مي‌دهد.

اين برنامه بدون سرآيند فايل <cmath> كامپايل نمي‌شود. تابع pow نيازمند دو آرگومان از نوع double است. دقت كنيد كه متغير year از نوع صحيح است. سرآيند <cmath> حاوي اطلاعاتي است كه به كامپايلر مي‌گويد تا مقدار year را بطور موقت تبديل به نوع double كند، قبل از اينكه تابع فراخواني شود. اين اطلاعات در نمونه اوليه تابع pow وجود دارند. در فصل ششم با چندين تابع كتابخانه math آشنا خواهيد شد.

1    // Fig. 5.6: fig05_06.cpp

2    // Compound interest calculations with for.

3    #include <iostream>

4    using std::cout;

5    using std::endl;

6    using std::fixed;

7     

8    #include <iomanip>

9    using std::setw; // enables program to set a field width

10  using std::setprecision;

11   

12  #include <cmath> // standard C++ math library

13  using std::pow; // enables program to use function pow

14   

15  int main()

16  {

17     double amount; // amount on deposit at end of each year

18     double principal = 1000.0; // initial amount before interest

19     double rate = .05; // interest rate

20   

21     // display headers

22     cout << "Year" << setw( 21 ) << "Amount on deposit" << endl;

23   

24     // set floating-point number format

25     cout << fixed << setprecision( 2 );

26   

27     // calculate amount on deposit for each of ten years

28     for ( int year = 1; year <= 10; year++ )

29     {

30        // calculate new amount for specified year

31        amount = principal * pow( 1.0 + rate, year );

32   

33        // display the year and the amount

34        cout << setw( 4 ) << year << setw( 21 ) << amount << endl;

35     } // end for

36   

37     return 0; // indicate successful termination

38  } // end main

 

 

 

شكل 6-5 | عبارت for براي محاسبه سود سرمايه ‌گذاري.

 

 

خطاي برنامه‌نويسي

بطور كلي، فراموش كردن الحاق فرآيند سرآيند مورد نياز به هنگام استفاده از توابع كتابخانه استاندارد (همانند <cmatch> ) خطاي كامپايل بدنبال خواهد داشت.

 

 

دقت به هنگام استفاده از نوع double در محاسبات مالي

دقت كنيد كه متغيرهاي amount و principal و rate از نوع double هستند. به اين دليل از اين نوع استفاده كرده‌ايم كه مي‌خواهيم به بخش‌هاي كسري رسيدگي كرده و به نوعي نياز داريم كه امكان انجام محاسبات دقيق در زمينه مسائل مالي را فراهم آوريم. متاسفانه اين نوع مي‌تواند مشكل‌ساز شود. در اين بخش با يك توضيح ساده شاهد خواهيد بود كه چگونه به هنگام استفاده از نوع float يا double در نمايش‌هاي مالي مي‌توانيم دچار اشتباه شويم (فرض كنيد از setprecision(2) براي مشخص كردن دو رقم دقت به هنگام چاپ استفاده كرده‌ايم): دو مبلغ دلاري ذخيره شده در ماشين مي‌تواند 14.234 (كه 14.23 چاپ مي‌شود) و 18.673 (كه 18.67 چاپ خواهد شد) را توليد كند. زمانيكه اين مبالغ با هم جمع شوند، مجموع داخلي 32.907 توليد مي‌شود كه بصورت 32.91 چاپ مي‌گردد. از اينرو نتيجه چاپي به صورت زير ظاهر خواهد شد.

14.23  

18.67    +

32.91

اما در صورتيكه خودمان اين اعداد را با هم جمع كنيم مجموع 32.90 را بدست خواهيم آورد. پس مشكلي در كار است.

 

 

برنامه‌نويسي ايده‌ال

از متغيرهاي نوع float يا double براي انجام محاسبات مالي استفاده نكنيد. عدم دقت كافي در چنين اعدادي مي‌تواند در نتيجه توليدي از محاسبات مالي، شما را دچار مشكل كند.

 

 

كنترل كننده‌هاي جريان براي قالب‌بندي كردن خروجي عددي

عبارت خروجي در خط 25 قبل از حلقه for و عبارت خروجي در خط 34 در حلقه for تركيب شده‌اند تا مقادير متغيرهاي year و amount با قالب‌بندي (فرمت) تصريح شده توسط كنترل‌كننده‌هاي جريان پارامتري شده setprecision و setw و كنترل‌كننده استريم غيرپارامتري fixed چاپ شوند. كنترل‌كننده استريم setw(4) مشخص مي‌كند كه بايد مقدار خروجي بعدي به طول فيلد مشخص شده يعني 4 به نمايش درآيد، يعني، cout مقدار را با حداقل 4 موقعيت كاراكتري چاپ خواهد كرد. اگر مقداري كه چاپ مي‌شود، كمتر از 4 كاراكتر طول داشته باشد، مقدار از سمت راست تراز مي‌شود (بطور پيش‌فرض). اگر مقدار خروجي بيش از 4 كاراكتر طول داشته باشد، طول فيلد گسترش مي‌يابد تا به كل مقدار جا دهد. براي تاكيد بر اين نكته كه مقادير در خروجي بايد از سمت چپ تراز شوند، كافيست از كنترل‌كننده استريم غيرپارامتري left استفاده شود (در سرآيند <iostream> قرار دارد). ترازبندي از سمت راست را مي‌توان با استفاده از كنترل‌كننده استريم غيرپارامتري right بدست آورد.

قالب‌بندي‌هاي ديگر، در عبارات خروجي بر اين نكته دلالت مي‌كنند كه متغير amount بصورت يك مقدار با نقطه ثابت با يك نقطه ديسمال (تصريح‌ شده در خط 25 با كنترل‌كننده استريم fixed ) تراز از سمت راست در فيلدي به ميزان 21 كاراكتر (تصريح شده در خط 34 با setw(21) ) و با دقت دو رقم در سمت راست نقطه ديسمال (تصريح شده در خط 25 با كنترل‌كننده setprecision(2) ) چاپ خواهد شد. كنترل‌كننده‌هاي استريم fixed و setprecision را بر روي استريم خروجي (يعني cout ) و قبل از حلقه for اعمال كرده‌ايم چرا كه اين تنظيمات قالب‌بندي تا زمانيكه تغيير نيافته‌اند ثابت باقي مي‌مانند، به چنين تنظيماتي، تنظيمات چسبنده مي‌گويند. از اينرو، نيازي ندارند در هر بار تكرار حلقه بكار گرفته شوند. با اين همه، طول فيلد (ميدان) مشخص شده با setw فقط بر مقدار بعدي در خروجي بكار گرفته مي‌شود. در فصل پانزدهم در ارتباط با قابليت‌هاي قالبندي ورودي/خروجي زبان C++ صحبت خواهيم كرد.

به عبارت 1.0 + rate كه بصورت يك آرگومان در تابع pow و در بدنه عبارت for قرار دارد، توجه كنيد. در واقع، اين محاسبه همان نتيجه را در زمان هر تكرار حلقه توليد مي‌كند، از اينرو تكرار بي‌موردي صورت گرفته و بايد اين عبارت يكبار و قبل از حلقه محاسبه شود.

 

 

كارايي

برخي از كامپايلرها حاوي ويژگي‌هاي بهينه‌سازي هستند كه سبب افزايش كارايي كد نوشته شده توسط شما مي‌شوند، اما بهتر است از همان مرحله شروع كار كدنويسي، كدهاي خود را كارآمد بنويسيم.

 

 

كارايي

از قراردادن عبارات محاسباتي در داخل حلقه كه مقدار آنها در هر بار اجراي حلقه تغييري نمي‌يابد خودداري كنيد. چنين عباراتي فقط يكبار و قبل از حلقه بايد ارزيابي شوند.

 

 

 

5-5عبارت تكرار do...while

عملكرد عبارت تكرار do...while همانند عبارت while است. در عبارت while ، شرط تكرار حلقه در ابتداي حلقه تست مي‌شود، قبل از اينكه بدنه حلقه به اجرا درآيد. عبارت do...while شرط تكرار حلقه را پس از اجراي حلقه تست مي‌كند. از اينرو، در يك عبارت do...while ، هميشه بدنه حلقه حداقل يكبار به اجرا در مي‌آيد. اگر فقط يك عبارت در بدنه وجود داشته باشد، استفاده از براكت‌ها در do...while ضرورتي ندارد. با اين همه، معمولا براي اجتناب از سردرگمي مابين عبارات while و do...while از براكت‌ها استفاده مي‌شود. براي مثال،

while ( شرط )

نشان‌دهنده سرآيند عبارت while است. يك عبارت do...while بدون براكت‌ها در اطراف بدنه خود (با يك عبارت) بصورت زير خواهد بود

do

  عبارت

while ( شرط );

كه مي‌تواند باعث اشتباه شود. خط آخر، مي‌تواند توسط خواننده به غلط بعنوان يك عبارت while با عبارت تهي تفسير شود (وجود سيمكولن در انتهاي شرط). از اينرو، براي اجتناب از اشتباه، بهتر است در يك عبارت do...while با يك عبارت از براكت‌ها استفاده شود:

do {

     عبارت ;

} while ( شرط );

در برنامه شكل 7-5 از يك عبارت do...while براي چاپ مقادير از 10 تا 1 استفاده شده است. عبارت do...while در خطوط 11-15 اعمال شده است. در اولين برخورد برنامه با اين عبارت، خط 13 اجرا شده و مقدار متغير counter (با مقدار 1 ) چاپ شده، سپس counter يك واحد افزايش مي‌يابد (خط 14 ) . سپس شرط در خط 15 ارزيابي مي‌شود. اكنون متغير counter حاوي مقدار 2 است كه كمتر يا مساوي با 10 مي‌باشد، چون شرط برقرار است، عبارت do...while مجدداً اجرا مي‌شود. در بار دهم كه عبارت اجرا شد، عبارت خط 13 مقدار 10 را چاپ كرده و در خط 14 ، مقدار counter به 11 افزايش مي‌يابد. در اين لحظه، شرط موجود در خط 15 ، نادرست ارزيابي شده و برنامه از عبارت do...while خارج مي‌شود و برنامه به سراغ عبارت پس از حلقه مي‌رود (خط 17 ) .

1    // Fig. 5.7: fig05_07.cpp

2    // do...while repetition statement.

3    #include <iostream>

4    using std::cout;

5    using std::endl;

6     

7    int main()

8    {

9       int counter = 1; // initialize counter

10   

11     do

12     {

13        cout << counter << " "; // display counter

14        counter++; // increment counter

15     } while ( counter <= 10 ); // end do...while

16   

17     cout << endl; // output a newline

18     return 0; // indicate successful termination

19  } // end main

شكل 7-5 | عبارت تكرار do...while

 

 

دياگرام فعاليت UML عبارت do...while

شكل 8-5 حاوي دياگرام فعاليت UML براي عبارت do...while است. اين دياگرام به وضوح نشان مي‌دهد كه شرط تكرار حلقه حداقل پس از يك بار اجراي عبارات موجود در بدنه حلقه، ارزيابي نخواهد شد. اين دياگرام فعاليت را با عبارت while بكار رفته در شكل 6-4 مقايسه كنيد. مجدداً، توجه كنيد كه اين دياگرام حاوي يك نماد وضعيت عمل و تصميم‌گيري است.

شكل 8-5 | دياگرام فعاليت UML عبارت تكرار do...while

6-5عبارت چند انتخابي switch

در فصل گذشته، در ارتباط با عبارت تك انتخابي if و دو انتخابي if...else صحبت كرديم. زبان C++ داراي عبارت چند انتخابي switch براي رسيدگي به چنين شرايطي است.

كلاس GradeBook با عبارت switch

در مثال بعدي، مبادرت به عرضه يك نسخه بهبود يافته از كلاس GradeBook معرفي شده در فصل سوم و فصل چهارم مي‌كنيم. نسخه جديد از كاربر مي‌خواهد تا مجموعه‌اي از امتيازات حرفي را وارد كرده، سپس تعداد دانشجوياني كه هر كدام امتيازي دريافت كرده‌اند، به نمايش در مي‌آورد. اين كلاس از يك عبارت switch براي تعيين اينكه امتياز وارد شده يك A ، B ، C ، D يا F مي‌باشد و افزايش مقدار شمارنده امتياز وارد شده، استفاده مي‌كند. كلاس GradeBook در برنامه شكل 9-5 تعريف شده و تعريف تابع عضو آن در برنامه شكل 10-5 قرار دارد. شكل 11-5 نمايشي از اجراي نمونه برنامه همراه ورودي‌ها و خروجي برنامه main است كه از كلاس GradeBook براي پردازش امتيازات استفاده مي‌كند. همانند نسخه‌هاي قبلي تعريف كلاس، تعريف كلاس GradeBook در شكل 9-5 حاوي نمونه اوليه تابع براي توابع عضو setCourseName در خط 13 ، getCourseName در خط 14 و displayMessage در خط 15 به همراه سازنده كلاس در خط 12 است. همچنين در تعريف كلاس عضو داده courseName بصورت private اعلان شده است (خط 19 ).

كلاس GradeBook در شكل9-5 داراي پنج عضو داده private است (خطوط 20-14 )، متغيرهاي شمارنده براي هر امتياز (يعني براي A ، B ، C ، D و F ) . همچنين كلاس داراي دو تابع عضو public بنام‌هاي inputGrades و displayGradeReport است. تابع عضو inputGrade (اعلان شده در خط 16 ) مبادرت خواندن حروف امتيازي به تعداد اختياري از طرف كاربر با روش مقدار مراقبتي مي‌كند و شمارنده امتياز مقتضي را براي هر امتياز وارد شده به روز مي‌نمايد. تابع عضو displayGradeReport (اعلان شده در خط 17 ) گزارشي از تعداد دانشجويان دريافت‌كننده هر امتياز به نمايش در مي‌آورد.

1    // Fig. 5.9: GradeBook.h

2    // Definition of class GradeBook that counts A, B, C, D and F grades.

3    // Member functions are defined in GradeBook.cpp

4     

5    #include <string> // program uses C++ standard string class

6    using std::string;

7     

8    // GradeBook class definition

9    class GradeBook

10  {

11  public:

12     GradeBook( string ); // constructor initializes course name

13     void setCourseName( string ); // function to set the course name

14     string getCourseName(); // function to retrieve the course name

15     void displayMessage(); // display a welcome message

16     void inputGrades(); // input arbitrary number of grades from user

17     void displayGradeReport(); // display a report based on the grades

18  private:

19     string courseName; // course name for this GradeBook

20     int aCount; // count of A grades

21     int bCount; // count of B grades

22     int cCount; // count of C grades

23     int dCount; // count of D grades

24     int fCount; // count of F grades

25  }; // end class GradeBook

شكل 9-5 | تعريف كلاس GradeBook .

فايل كد منبع GradeBook.cpp در شكل10-5 حاوي تعريف تابع عضو كلاس GradeBook است. توجه كنيد زمانيكه يك شي GradeBook براي اولين بار ايجاد مي‌شود و هنوز هيچ امتيازي وارد نشده است، خطوط 16-20 در سازنده مبادرت به مقداردهي اوليه پنج شمارنده امتياز با صفر مي‌كنند. همانطوري كه بزودي خواهيد ديد، اين شمارنده‌ها در تابع عضو inputGrade و بعنوان امتيازهاي ورودي كاربر افزايش مي‌يابند. تعاريف توابع عضو setCourseName ، getCourseName و displayMessage با نسخه‌هاي قبلي موجود در كلاس GradeBook يكسان هستند. اجازه دهيد تا نگاهي به توابع عضو جديد در GradeBook داشته باشيم.

1    // Fig. 5.10: GradeBook.cpp

2    // Member-function definitions for class GradeBook that

3    // uses a switch statement to count A, B, C, D and F grades.

4    #include <iostream>

5    using std::cout;

6    using std::cin;

7    using std::endl;

8     

9    #include "GradeBook.h" // include definition of class GradeBook

10   

11  // constructor initializes courseName with string supplied as argument;

12  // initializes counter data members to 0

13  GradeBook::GradeBook( string name )

14  {

15     setCourseName( name ); // validate and store courseName

16     aCount = 0; // initialize count of A grades to 0

17     bCount = 0; // initialize count of B grades to 0

18     cCount = 0; // initialize count of C grades to 0

19     dCount = 0; // initialize count of D grades to 0

20     fCount = 0; // initialize count of F grades to 0

21  } // end GradeBook constructor

22   

23  // function to set the course name; limits name to 25 or fewer characters

24  void GradeBook::setCourseName( string name )

25  {

26     if ( name.length() <= 25 ) // if name has 25 or fewer characters

27        courseName = name; // store the course name in the object

28     else // if name is longer than 25 characters

29    { // set courseName to first 25 characters of parameter name

30        courseName = name.substr( 0, 25 ); // select first 25 characters

31        cout << "Name \"" << name << "\" exceeds maximum length (25).\n"

32           << "Limiting courseName to first 25 characters.\n" << endl;

33     } // end if...else

34  } // end function setCourseName

35   

36  // function to retrieve the course name

37  string GradeBook::getCourseName()

38  {

39     return courseName;

40  } // end function getCourseName

41   

42  // display a welcome message to the GradeBook user

43  void GradeBook::displayMessage()

44  {

45     // this statement calls getCourseName to get the

46     // name of the course this GradeBook represents

47     cout << "Welcome to the grade book for\n" << getCourseName() << "!\n"

48        << endl;

49  } // end function displayMessage

50   

51  // input arbitrary number of grades from user; update grade counter

52  void GradeBook::inputGrades()

53  {

54     int grade; // grade entered by user

55   

56     cout << "Enter the letter grades." << endl

57        << "Enter the EOF character to end input." << endl;

58   

59     // loop until user types end-of-file key sequence

60     while ( ( grade = cin.get() ) != EOF )

61     {

62       // determine which grade was entered

63        switch ( grade ) // switch statement nested in while

64        {

65           case 'A': // grade was uppercase A

66           case 'a': // or lowercase a

67              aCount++; // increment aCount

68              break; // necessary to exit switch

69   

70           case 'B': // grade was uppercase B

71           case 'b': // or lowercase b

72               bCount++; // increment bCount   

73              break; // exit switch

74   

75           case 'C': // grade was uppercase C

76          case 'c': // or lowercase c

77              cCount++; // increment cCount   

78              break; // exit switch

79   

80           case 'D': // grade was uppercase D

81           case 'd': // or lowercase d

82              dCount++; // increment dCount   

83              break; // exit switch

84   

85           case 'F': // grade was uppercase F

86           case 'f': // or lowercase f

87              fCount++; // increment fCount   

88              break; // exit switch

89   

90           case '\n': // ignore newlines, 

91           case '\t': // tabs,

92           case ' ': // and spaces in input

93              break; // exit switch

94   

95           default: // catch all other characters

96              cout << "Incorrect letter grade entered."

97                 << " Enter a new grade." << endl;

98              break; // optional; will exit switch anyway

99       } // end switch

100          } // end while

101       } // end function inputGrades

102        

103       // display a report based on the grades entered by user

104       void GradeBook::displayGradeReport()

105       {

106          // output summary of results

107          cout << "\n\nNumber of students who received each letter grade:"

108             << "\nA: " << aCount // display number of A grades

109             << "\nB: " << bCount // display number of B grades

110             << "\nC: " << cCount // display number of C grades

111             << "\nD: " << dCount // display number of D grades

112             << "\nF: " << fCount // display number of F grades

113             << endl;

114       } // end function displayGradeReport

شكل 10-5 | كلاس GradeBook از عبارت switch براي شمارش امتيازات A, B, C, D و F استفاده مي‌كند.

خواندن كاراكترهاي ورودي

كاربر مبادرت به وارد كردن امتيازات حرفي براي يك واحد درسي در تابع inputGrades مي‌كند (خطوط 52-101 ). در سرآيند حلقه while در خط 60 ، ابتدا عبارت تخصيصي قرار گرفته در درون پرانتز ( grade = cin.get() ) اجرا مي‌شود. تابع cin.get() يك كاراكتر از صفحه كليد خوانده و آن را در متغير grade از نوع صحيح ذخيره مي‌سازد (اعلان شده در خط 54 ). معمولاً كاراكترها در متغيرهاي از نوع char ذخيره مي‌شوند، با اين همه، مي‌توان كاراكترها را در هر نوع داده صحيحي ذخيره كرد، چرا كه نشاندهنده 1 بايت صحيح در كامپيوتر هستند. از اينرو، مي‌توانيم براساس استفاده، با يك كاراكتر همانند يك مقدار صحيح يا بعنوان يك كاراكتر رفتار كنيم. براي مثال، عبارت

cout << "The character (" << 'a' << ") has the value"

<< static_cast< int > ( 'a' ) << endl;

مبادرت به چاپ كاراكتر a و مقدار صحيح آن بصورت زير مي‌كند:

The character (a) has the value 97

عدد صحيح 97 نشاندهنده شماره عددي كاراكتر a در كامپيوتر است. اكثر كامپيوترها از مجموعه كاراكتري ASCII (American Standard Code for Information Interchange) استفاده مي‌كنند، كه در اين مورد 97 نشاندهنده حرف كوچك 'a' است.

بطور كلي عبارات تخصيص‌دهنده مبادرت به تخصيص مقدار قرار گرفته در سمت راست به متغير قرار گرفته در سمت چپ علامت = مي‌كنند. بنابر اين مقدار عبارت تخصيصي grade=cin.get() همان مقدار برگشتي از سوي cin.get() بوده و به متغير grade تخصيص مي‌يابد. مي‌توان از عبارات تخصيص‌دهنده براي تخصيص يك مقدار به چندين متغير استفاده كرد. براي مثال در عبارت زير

a=b=c=0;

ابتدا تخصيص c=0 صورت مي‌گيرد چرا كه عملگر = داراي شركت‌پذيري از سمت راست به چپ است. سپس به متغير b مقدار تخصيصي c=0 ، تخصيص مي‌يابد (كه صفر است). سپس متغير a حاوي مقدار تخصيصي b=(c=0) خواهد شد كه آن هم صفر است. در برنامه، مقدار عبارت تخصيصي grade=cin.get() با مقدار EOF مقايسه مي‌شود (نمادي كه كوتاه شده عبارت "end-of-file" است). ما از EOF (كه معمولاً داراي مقدار -1 است) بعنوان مقدار مراقبتي استفاده‌ كرده‌ايم. با اين همه، نيازي به تايپ مقدار -1 يا تايپ حروف EOF بعنوان مقدار مراقبتي نداريد. بجاي آن از يك تركيب كليدي استفاده كرده‌ايم، كه نشاندهنده EOF در سيستم است. EOF يك ثابت سمبوليك سيستم است كه در سرآيند فايل <iostream> تعريف شده است. اگر مقداري به grade تخصيص دهيد كه معادل با EOF باشد، حلقه while در خطوط 60-100 بكار خود خاتمه مي‌دهد. نمايش كاراكترهاي وارد شده به اين برنامه را بصورت مقادير صحيح انتخاب كرده‌ايم، چرا كه EOF داراي يك مقدار صحيح است.

در سيستم‌هاي UNIX/Linux و برخي از سيستم‌هاي ديگر، EOF با تايپ

<ctrl> d

در يك خط وارد مي‌شود. اين عبارت به اين معني است كه كليد ctrl را فشار و پايين نگه داشته و سپس كليد d فشار داده شود. در سيستم‌هاي ديگر همانند Microsoft Windows ، مي‌توان EOF را با تايپ

<ctrl> z

وارد كرد.

 

 

قابليت حمل

كليدهاي تركيبي براي وارد كردن EOF به سيستم وابسته هستند.

در اين برنامه، كاربر امتيازها را توسط صفحه كليد وارد مي‌كند. زمانيكه كاربر كليد Enter  را فشار دهد، كاراكترها توسط تابع cin.get() خوانده مي‌شوند، يك كاراكتر در يك زمان. اگر كاراكتر وارد شده EOF نباشد، برنامه وارد عبارت switch مي‌شود (خط 63-99 ) كه شمارنده امتياز را متناسب با حرف وارد شده، يك واحد افزايش مي‌دهد.

 

 

جزئيات عبارت switch

عبارت switch متشكل از تعدادي برچسب case و يك حالت default اختياري است. از اين عبارت در اين مثال براي تعيين اينكه كدام شمارنده بايد براساس امتياز وارد شده افزايش يابد، استفاده شده است. زمانيكه كنترل برنامه به switch مي‌رسد، برنامه مبادرت به ارزيابي عبارت موجود در درون پرانتزها مي‌كند (يعني grade ) كه بدنبال كلمه كليدي switch قرار گرفته است (خط 63 ). به اين عبارت، عبارت كنترلي گفته مي‌شود. عبارت switch شروع به مقايسه مقدار عبارت كنترلي با هر برچسب case مي‌كند. فرض كنيد كه كاربر حرف C را بعنوان كد امتياز وارد كرده باشد. برنامه شروع به مقايسه C با هر case موجود در بدنه switch مي‌كند. اگر مطابقتي يافت شود (در خط 75 ، case 'C' )، برنامه عبارات موجود در آن case را به اجرا در مي‌آورد. در ارتباط با حرف ‍ C ، خط 77 مبادرت به افزايش cCount به مي ز ان يك واحد مي‌كند. عبارت break (خط 78 ) سبب مي‌شود كه كنترل برنامه به اولين عبارت پس از switch منتقل شود كه در اين برنامه كنترل به خط 100 انتقال مي‌يابد. اين خط، انتهاي بدنه حلقه while را نشان مي‌دهد (خطوط 60-100 )، از اينرو جريان كنترل به سمت شرط while در خط 60 مي‌رود تا تعيين نمايد كه آيا حلقه بايستي ادامه يابد يا خير.

case هاي موجود در عبارت switch بطور صريح مبادرت به تست حروف كوچك و بزرگ حرف‌هاي A ، B ، C ، D و F مي‌كنند. توجه كنيد كه case هاي موجود در خطوط 65-66 در تست مقادير 'A' و 'a' كاربرد دارند (هر دو نشاندهنده امتياز A مي‌باشند). ليست case ها در اين روش بصورت متوالي بوده و عبارتي مابين آنها وجود ندارد و به هر دو case اجازه مي‌دهد تا يك مجموعه از عبارات را به اجرا درآورند، زمانيكه عبارت كنترلي با 'A' يا 'a' ارزيابي شود، عبارات قرار گرفته در خطوط 67-68 اجرا خواهند شد. توجه كنيد كه هر case مي‌تواند چندين عبارت داشته باشد. عبارت انتخابي switch متفاوت از ديگر عبارات كنترلي بوده و نيازي به استفاده از براكت‌ها در اطراف چندين عبارت در هر case ندارد.

بدون عبارات break ، هر زمان كه مطابقتي در switch تشخيص داده شود، عبارات آن case و case هاي متعاقب آن اجرا خواهن د شد تا اينكه به يك عبارت break يا به انتهاي switch برسد. به اين حالت "fallingthrough" يا عدم دستيابي به نتيجه در case هاي بعدي گفته مي‌شود.

 

 

خطاي برنامه‌نويسي

نتيجه فراموش كردن عبارت break در مكاني كه به آن در switch نياز است، يك خطاي منطقي است.

 

 

خطاي برنامه‌نويسي

حذف فاصله مابين كلمه case و مقدار ارزش در يك عبارت switch خطاي منطقي است. براي مثال، نوشتن case3: بجاي case 3: يك برچسب بلااستفاده بوجود مي‌آورد.

 

 

تدارك ديدن حالت default

اگر هيچ مطابقتي مابين مقدار عبارت كنترلي و يك برچسب case پيدا نشود، حالت default اجرا خواهد شد (خطوط 95-98 ). در اين مثال از حالت default براي پردازش تمام مقادير عبارت كنترلي كه امتيازهاي معتبر نيستند، كاراكترهاي خط جديد، تب يا فاصله استفاده كرده‌ايم. اگر هيچ مطابقتي رخ ندهد، حالت default اجرا مي‌شود و خطوط 96-97 يك پيغام خطا به نمايش در مي‌آورند تا بر اين نكته دلالت كنند كه يك حرف امتيازي اشتباه وارد شده است. اگر هيچ مطابقتي در يك عبارت switch رخ ندهد و اين عبارت فاقد حالت default باشد، كنترل برنامه بسادگي به اجراي اولين عبارت پس از switch ادامه خواهد داد.

 

 

برنامه‌نويسي ايده‌ال

در عبارات switch حالت default را در نظر بگيريد. در حالت default برنامه‌نويس مي‌تواند مواردي كه براي پردازش موارد استثناء پيش مي‌آيند در نظر بگيرد. با اينكه مي‌توان به هر ترتيبي شرط‌هاي case و حالت default را در يك عبارت switch قرار داد، اما بهتر است ضابطه default در آخر قرار داده شود.

 

 

برنامه‌نويسي ايده‌ال

در يك عبارت switch كه default در انتهاي آن قرار دارد، نيازي نيست كه ضابطه default حاوي دستور break باشد. برخي از برنامه‌نويسان به منظور حفظ تقارن و وضوح بيشتر با ساير case ها ترجيح مي‌دهند از break در default استفاده كنند.

 

 

ناديده گرفتن كاركترهاي خط‌جديد،تب و فاصله در ورودي

خطوط 90-93 در عبارت switch شكل 10-5 سبب مي‌شوند تا برنامه كاراكترهاي خط جديد، تب و فاصله‌ها را در نظر نگيرد. خواندن يك كاراكتر در هر زمان مي‌تواند مشكل‌ساز شود. براي داشتن برنامه‌اي كه كاراكترها را بخواند، بايستي آنها را به كامپيوتر با فشردن كليد Enter صفحه‌كليد ارسال كنيم. با اينكار يك كاراكتر خط جديد (newline) در ورودي پس از كاراكترهايي كه مايل به پردازش آنها هستيم، وارد مي‌شود. غالباً براي اينكه برنامه بدرستي كار كند نياز است تا به اين كاراكتر رسيدگي شود. با قرار دادن case سابق‌الذكر در عبارت switch ، مانع از نمايش پيغام خطا در حالت default در هر بار مواجه شدن با كاراكترهاي خط جديد، تب (tab) يا فاصله در ورودي شده‌ايم.

 

 

خطاي برنامه‌نويسي

عدم پردازش كاراكترهاي خط جديد و ساير كاراكترهاي white-space در ورودي، زمانيكه در هر بار يك كاراكتر خوانده مي‌شود، مي‌تواند شما را با خطاهاي منطقي مواجه سازد.

 

 

تست كلاس GradeBook

برنامه شكل 11-5 يك شي GradeBook ايجاد مي‌كند (خط 9 ). خط 11 تابع عضو displayMessage شي را براي نمايش پيغام خوش‌آمدگويي به كاربر فراخواني مي‌كند. خط 12 تابع عضو inputGrades را براي خواندن مجموعه‌اي از امتيازها از سوي كاربر و شمارش تعداد دانشجويان دريافت‌كننده امتياز فراخواني مي‌كند. به پنجره خروجي/ورودي به نمايش درآمده در شكل 11-5 توجه كنيد كه يك پيغام خطا در واكنش به وارد كردن يك امتياز اشتباه (يعني E ) به نمايش درآورده است. خط 13 مبادرت به فراخواني تابع عضو displayGradeReport تعريف شده در خطوط 104-114 از شكل 10-5 مي‌كند و اين تابع نتيجه برنامه را براساس امتيازهاي وارد شده به نمايش در مي‌آورد.

1    // Fig. 5.11: fig05_11.cpp

2    // Create GradeBook object, input grades and display grade report.

3     

4    #include "GradeBook.h" // include definition of class GradeBook

5     

6    int main()

7    {

8       // create GradeBook object

9       GradeBook myGradeBook( "CS101 C++ Programming" );

10   

11     myGradeBook.displayMessage(); // display welcome message

12     myGradeBook.inputGrades(); // read grades from user

13     myGradeBook.displayGradeReport(); // display report based on grades

14     return 0; // indicate successful termination

15  } // end main

 

شكل 11-5 | ايجاد يك شي GradeBook و فراخواني توابع عضو آن.

 

 

دياگرام فعاليت UML عبارت switch

شكل 12-5 نشاندهنده دياگرام فعاليت UML يك عبارت چند انتخابي switch است. اكثر عبارات switch از يك break در هر case استفاده مي‌كنند تا به عبارت switch پس از پردازش آن case خاتمه دهند. شكل12-5 بر استف اده از عبارت break  در دياگرام فعاليت تاكيد دارد. بدون عبارت break كنترل به اولين عبارت پس از ساختار switch پس از اينكه يك case پردازش شد، منتقل نمي‌شود. بجاي آن كنترل به سراغ اجراي case بعدي مي‌رود.

اين دياگرام به وضوح نشان مي‌دهد كه عبارت break در انتهاي يك case سبب انتقال بلافاصله كنترل به خارج از عبارت switch مي‌شود. مجدداً توجه كنيد كه اين دياگرام حاوي نمادهاي وضعيت عمل و تصميم‌گيري است. همچنين توجه كنيد كه در اين دياگرام از نمادهاي ادغام براي انتقال از عبارات break به وضعيت پاياني استفاده شده است.

به هنگام استفاده از عبارت switch ، بخاطر داشته باشيد كه مي‌توان از آن فقط براي تست يك مقدار ارزشي ثابت استفاده كرد. هر تركيبي از ثابت‌هاي كاراكتري و ثابت‌هاي عددي صحيح، بصورت يك ثابت عددي صحيح ارزيابي مي‌شوند. يك ثابت كاراكتري بصورت يك كاراكتر خاص در ميان علامت نقل قول همانند 'A' است. يك ثابت عددي، يك مقدار عددي صحيح است. همچنين هر برچسب case مي‌تواند تعيين‌كننده يك عبارت ارزشي ثابت باشد.

 

خطاي برنامه‌نويسي

مشخص كردن يك عبارت همراه با متغيرها همانند a+b در برچسب case يك switch ، خطاي نحوي است.

 

خطاي برنامه‌نويسي

تدارك ديدن برچسب‌هاي يكسان در يك عبارت switch خطاي كامپايل بدنبال خواهد داشت. همچنين قرار دادن عبارات مختلف در case ها كه مقدار آنها يكسان ارزيابي گردند نيز خطاي كامپايل بوجود مي‌آورد. براي مثال قرار دادن case4+ 1: و case3+2: در يك عبارت switch خطاي كامپايل است چرا كه هر دو آنها برابر case 5: هستند.

شكل 12-5 | دياگرام فعاليت UML ساختار switch با عبارت break .

 

 

نكاتي در ارتباط با نوع داده

زبان C++ داراي نوع داده با سايزهاي مختلف است. براي مثال، امكان دارد برنامه‌هاي مختلف، به اعداد صحيح با سايزهاي متفاوت نياز داشته باشند. C++ داراي چندين نوع داده براي عرضه مقادير صحيح است. طول مقادير صحيح براي هر نوع بستگي به سخت‌افزار كامپيوتر دارد. علاوه بر نوع‌هاي int و char ، زبان C++ نوع‌هاي short (كوتاه شده short int ) و long (كوتاه شده long int ) را در نظر گرفته است. حداقل محدودة مقادير صحيح از نوع short از -32.768 تا 32.767 مي‌باشد. براي اكثر محاسبات صحيح، مقادير صحيح از نوع long كافي هستند. حداقل محدودة مقادير از نوع long از       -2.147.483.648 تا 2.147.483.647 است. بر روي اكثر كامپيوترها، مقادير int معادل short يا long هستند. محدودة مقادير براي يك int حداقل برابر با مقادير short بوده و بيشتر از مقادير long نمي‌باشند. از نوع داده char مي‌توان براي عرضه هر كاراكتري در مجموعه كاراكتري كامپيوتر استفاده كرد. همچنين مي‌توان از آن براي نمايش مقادير صحيح كوچك استفاده كرد.

 

قابليت حمل

بدليل اينكه int ها مي‌توانند مابين سيستم‌ها سايز متفاوتي داشته باشند، اگر انتظار داريد محاسبه‌اي سبب توليد مقداري بيش از محدودة 32.767 تا -32.763 نمايد از نوع long استفاده كرده و در صورت امكان برنامه را بر روي سيستم‌هاي مختلف اجرا كنيد.

 

 

كارايي

اگر استفاده بهينه از حافظه مطرح باشد، مي‌توانيد از مقادير صحيح با سايز كوچك استفاده كنيد.

 

 

7-5  عبارات break و continue

علاوه بر عبارات كنترلي و انتخابي، زبان C++ عبارات break و continue را براي ايجاد تغيير در جريان كنترل در نظر گرفته است. بخش بعدي شما را با نحوه استفاده از break در خاتمه دادن به اجراي يك عبارت switch آشنا خواهد كرد. همچنين اين بخش در مورد نحوه استفاده از break در يك عبارت تكرار مطالبي عرضه خواهد كرد.

 

 

عبارت break

دستور break با اجرا در عبارات while ,for ,do..while يا switch سبب مي‌شود تا كنترل بلافاصله از عبارت خارج شده، و برنامه با اولين عبارت پس از عبارت ادامه مي‌يابد. معمولا از دستور break براي خارج شدن زود هنگام از حلقه يا پرش از مابقي عبارت switch (همانند برنامه 10-5) استفاده مي‌شود. برنامه شكل 13-5 به توضيح عملكرد دستور break در عبارت تكرار for پرداخته است (خط 14 ).

1    // Fig. 5.13: fig05_13.cpp

2    // break statement exiting a for statement.

3    #include <iostream>

4    using std::cout;

5    using std::endl;

6     

7    int main()

8    {

9       int count; // control variable also used after loop terminates

10   

11     for ( count = 1; count <= 10; count++ ) // loop 10 times

12     {

13        if ( count == 5 ) // if count is 5,

14           break;         // terminate loop

15   

16        cout << count << " ";

17     } // end for

18   

19     cout << "\nBroke out of loop at count = " << count << endl;

20     return 0; // indicate successful termination

21  } // end main

 

 

 

 

13-5 | عبارت break در يك عبارت for .

 

زمانيكه عبارت if تشخيص مي‌دهد كه count برابر 5 است، دستور break اجرا مي‌شود. با اينكار عبارت for خاتمه مي‌يابد و برنامه به خط 19 منتقل مي‌شود (بلافاصله پس از عبارت for )، و پيغامي مبني بر اينكه متغير كنترلي به هنگام خاتمه حلقه چه مقداري داشته به نمايش در مي‌آيد. عبارت for بطور كامل و فقط چهار بار بجاي ده بار اجرا مي‌شود. توجه كنيد كه متغير كنترلي count در خارج از سرآيند عبارت for تعريف شده است، از اينروست كه مي‌توانيم از متغير كنترلي هم در بدنه حلقه و هم پس از آن استفاده كنيم.

عبارت countinue

دستور continue ، با اجرا شدن در عبارات while ,for يا do..while از مابقي عبارات موجود در درون بدنه عبارت پرش كرده و كار را با حلقه بعد دنبال مي‌كند. در عبارات while و do..while ، شرط تكرار حلقه بلافاصله پس از اجراي continue ارزيابي مي‌شود. در عبارات for ، بخش افزايش دهنده، پس از ارزيابي شرط تكرار حلقه صورت مي‌گيرد. اين مورد تنها اختلاف مابين for و while است. قرار دادن اشتباه continue قبل از بخش افزايش در while مي‌تواند سبب بوجود آمدن يك حلقه بي‌نهايت شود.

در برنامه 14-5 از دستور continue در يك عبارت for استفاده شده (خط 12 ) تا از عبارت خروجي خط  14 پرش شود، زمانيكه عبارت if در خطوط  11-12 تشخيص دهد كه مقدار count برابر 5 شده است. زمانيكه عبارت continue اجرا شود، برنامه از مابقي بدنه for پرش خواهد كرد. كنترل برنامه با افزايش متغير كنترلي عبارت for ادامه مي‌يابد و بدنبال آن شرط تكرار حلقه صورت مي‌گيرد تا تعيين كند آيا بايستي حلقه ادامه يابد يا خير.

1    // Fig. 5.14: fig05_14.cpp

2    // continue statement terminating an iteration of a for statement.

3    #include <iostream>

4    using std::cout;

5    using std::endl;

6     

7    int main()

8    {

9       for ( int count = 1; count <= 10; count++ ) // loop 10 times

10     {

11        if ( count == 5 ) // if count is 5,

12           continue;      // skip remaining code in loop

13   

14        cout << count << " ";

15     } // end for

16   

17     cout << "\nUsed continue to skip printing 5" << endl;

18     return 0; // indicate successful termination

19  } // end main

 

 

 

 

 

شكل 14-5 | عبارت continue در يك عبارت for .

در بخش 3-5 مشخص كرديم كه عبارت while مي‌توانند در بسياري از موارد بجاي عبارت for بكار گرفته شود. يك استثناء زماني رخ مي‌دهد كه عبارت افزايش‌دهنده در ساختار while بدنبال دستور continue  آمده باشد. در اينحالت، عمل افزايش قبل از تست شرط ادامه حلقه اجرا نخواهد شد.

 

 

برنامه‌نويسي ايده‌ال

تعدادي از برنامه‌نويسان احساس مي‌كنند كه استفاده از break و continue برخلاف قواعد برنامه‌نويسي ساخت‌يافته است، چراكه مي‌توان بجاي بكارگيري اين عبارات از تكنيك‌هاي برنامه‌نويسي ساخت‌يافته استفاده كرد به نوعي كه ديگر نيازي به استفاده از آنها نباشد.

 

 

كارائي

اگر عبارات break و continue درست بكار گرفته شوند، نسبت به تكنيك‌هاي ساخت‌يافته سريعتر اجرا مي‌شوند.

 

8-5  عملگرهاي منطقي

تا بدين جا، فقط به معرفي شرط‌هاي ساده‌اي، همچون count <= 10 ، total > 1000 و number != sentinelValue پرداخته‌ايم و هر عبارت انتخاب و تكرار فقط اقدام به ارزيابي يك شرط با يكي از عملگرهاي > ، < ، >= ، <= ، == و != مي‌كرد. براي تصميم‌گيري كه بر مبناي ارزيابي از چندين شرط بود، اين بررسي‌ها را در عبارات جداگانه يا عبارتهاي if يا if…then  تودرتو انجام مي‌داديم.

به منظور رسيدگي موثرتر به شرط‌هاي پيچيده ، C++ عملگرهاي منطقي در نظر گرفته است . عملگرها عبارتند از && ( AND منطقي || ( OR منطقي ) و ! ( NOT منطقي ، يا نفي منطقي) . با طرح مثال‌هاي به بررسي عملكرد اين عملگرها مي‌پردازيم .

عملگر AND  منطقي (&&)

فرض كنيد مي‌خواهيم از برقرار بودن دو شرط قبل از اينكه برنامه مسير مشخص ي را براي اجرا انتخاب كند، مطمئن شويم. در چنين حالتي، مي‌توانيم از عملگر && و بصورت زير استفاده كنيم:

if ( gender == 1 && age >= 65 )

seniorFemales++;

اين عبارت if   متشكل از دو شرط ساده است شرط gender == 1 تعيين مي‌كند كه آيا شخص مونث است يا خير و شرط age >= 65 مشخص مي‌كند كه آيا شخص شهروند مسني است يا خير. ابتدا اين دو شرط ساده مورد ارزيابي قرار مي‌گيرند، چرا كه تقدم عملگرهاي ==   و >= به نسبت && در مرتبه بالاتري قرار دارند. سپس عبارت if   به بررسي تركيبي شرط زير مي‌پردازد

gender == 1 && age >= 65

اگر فقط و فقط اگر هر دو شرط برقرار باشند، برقراري اين شرط درست ارزيابي خواهد شد. هنگامي كه تركيب اين شرط درست باشد، به مقدار seniorFemales يك واحد افزوده مي‌شود. با اين وجود، اگر فقط يكي از اين دو شرط يا يكي از آنها برقرار نباشد (درست نباشد)، برنامه از انجام عمل افزايش صرفنظر كرده و به اجراي برنامه پس از عبارت if مي‌پردازد. عبارت قبل را مي‌توان با استفاده از پرانتزها بهتر كرد:

( gender == 1 ) && ( age >= 65 )

 

 

خطاي برنامه‌نويسي

اگر چه 3<x<7 در جبر شرط درستي است، اما بدرستي در C++ ارزيابي نمي‌شود. براي ارزيابي صحيح در C++ بايد از    (3 < x && x < 7) استفاده كرد.

جدول شكل 15-5 عملكرد عملگر && را نشان مي‌دهد. در اين جدول چهار حالت ممكنه از تركيب مقادير false و true بر روي عبارت1 و عبارت2 ليست شده‌اند. به اين نوع جداول، جدول درستي گفته مي‌شود.

عبارت1

عبارت2

عبارت1 && عبارت2

false

false

false

false

true

false

true

false

false

true

true

true

شكل 15-5| جدول درستي عملگر && .

عملگر OR  منطقي ( ||)

حال اجازه دهيد تا به بررسي عملگر || ( OR شرطي) بپردازيم. فرض كنيد مايل هستيم تا قبل از انجام يك عمل مشخص از برقرار بودن هر دو شرط يا يكي از شرط‌ها (درست بودن) مطمئن شويم. در بخشي از برنامه زير، از عملگر || استفاده شده است:

if ((semesterAverage >= 90) || (finalExam >= 90))

cout << “Student grade is A” << endl;

اين عبارت هم متشكل از دو شرط ساده است. با ارزيابي شرط semesterAverage >= 90 مشخص مي‌شود كه آيا دانشجو به دليل حفظ كارايي خود در طول ترم قادر به دريافت امتياز “A” بوده است يا خير. شرط finalExam >= 90 تعيين مي‌كند كه آيا دانشجو در آزمون پايان ترم امتياز “A” بدست آورده يا خير. سپس عبارت if  به بررسي تركيبي شرط زير مي‌پردازد

(semesterAverage >= 90) || (finalExam >= 90)

و اگر يكي از شرط‌ها يا هر دو آنها برقرار باشند، نمره “A” به دانشجو اهداء مي‌شود. دقت كنيد كه فقط در صورت برقرار نبودن هر دو شرط ( false بودن)، عبارت “Student grade is A” چاپ نخواهد شد. جدول شكل 16-5، جدول درستي عملگر OR منطقي است.

  عبارت1

عبارت2

عبارت1 || عبارت2

false

false

false

false

true

true

true

false

true

true

true

true

شكل 16-5 | جدول درستي عملگر ||

 عملگر && از تقدم بالاتري نسبت به عملگر || برخوردار است. هر دو عملگر از چپ به راست ارزيابي مي‌شوند. يك عبارت حاوي عملگرهاي && يا || فقط تا زمان شناخت درست بودن يا نبودن ارزيابي مي‌گردد. از اينرو، در ارزيابي عبارت

(gender == 1) && (age >= 65)

اگر gender معادل با " 1 " نباشد (در اينصورت كل عبارت برقرار نخواهد بود)، ارزيابي عبارت دوم بيهود خواهد بود چرا كه شرط عبارت اول برقرار نيست. ارزيابي عبارت يا شرط دوم فقط زماني رخ مي‌دهد كه gender برابر " 1 " باشد (براي برقرار بودن كل عبارت هنوز هم بايد شرط age >= 65 برقرار گردد). اين ويژگي در ارزيابي عبارات && و || ارزيابي اتصالي ناميده مي‌شود. در ضمن اين ويژگي سبب افزايش كارائي مي‌گردد.

كارائي

در عبارتي كه از عملگر && استفاده مي‌كند و شرط‌ها از هم متمايز هستند، آن شرطي را كه احتمال برقرار نبودن آن بيشتر است در سمت چپ شرط قرار دهيد. در عبارتي كه از عملگر || استفاده مي‌كند، شرطي را كه احتمال برقرار بودن آن بيشتر است در سمت چپ شرط قرار دهيد. در اينحالت از ارزيابي اتصالي استفاده شده و زمان اجراي برنامه كاهش مي‌يابد.

عملگر نفي منطقي (!)

عملگر ! (نفي منطقي ) به برنامه‌نويس امكان مي‌دهد تا نتيجه يك شرط را "معكوس" كند. برخلاف عملگرهاي منطقي && و || كه از دو شرط استفاده مي‌كنند (عملگرهاي باينري هستند)، عملگر منطقي نفي يك عملگر غيرباينري است و فقط با يك عملوند بكار گرفته مي‌شود. اين عملگر قبل از يك شرط جاي داده مي‌شود. براي مثال به عبارت زير دقت كنيد:

if ( !( grade == sentinelValue ) )

cout << “The next grade is ” << grade << endl;

 

وجود پرانتزها در اطراف شرط grade == sentinelValue ضروري است، چراكه تقدم عملگر نفي از عملگر برابري (تساوي) بالاتر است. جدول شكل 17-5 جدول درستي عملگر نفي است.

 

عبارت

! عبارت

false

true

true

false

شكل 17-5 | جدول درستي عملگر نفي.

مثالي از عملگرهاي منطقي

برنامه شكل 18-5 به توصيف عملگرهاي منطقي مطرح شده در جداول درستي مي‌پردازد. در خروجي هر عبارت ارزيابي شده و نتيجه بولي آن بنمايش در آمده است. بطور پيش فرض، مقادير بولي true و false توسط cout و عملگر درج بصورت 1 و 0 بنمايش در آمده‌اند. در خط 11 ، از boolalpha كنترل كننده استريم استفاده كرده‌ايم. تا مشخص كنيم كه مقدار هر عبارت بولي بايستي با كلمه "true" يا "false" بنمايش در آيد. براي مثال، نتيجه عبارت false && false در خط 12 مقدار false است، از اينرو در دومين خط خروجي كلمه "false" بكار گرفته شده است. خطوط 11-15 جدول درستي && را تشكيل مي‌دهند. خطوط 18-22 توليد كننده جدول درستي || هستند. خطوط 25-27 جدول درستي ! را ايجاد مي‌كنند.

1    // Fig. 5.18: fig05_18.cpp

2    // Logical operators.

3    #include <iostream>

4    using std::cout;

5    using std::endl;

6    using std::boolalpha; // causes bool values to print as "true" or "false"

7     

8    int main()

9    {

10     // create truth table for && (logical AND) operator

11     cout << boolalpha << "Logical AND (&&)"

12        << "\nfalse && false: " << ( false && false )

13        << "\nfalse && true: " << ( false && true )

14         << "\ntrue && false: " << ( true && false )

15        << "\ntrue && true: " << ( true && true ) << "\n\n";

16   

17     // create truth table for || (logical OR) operator

18     cout << "Logical OR (||)"

19        << "\nfalse || false: " << ( false || false )

20        << "\nfalse || true: " << ( false || true )

21        << "\ntrue || false: " << ( true || false )

22        << "\ntrue || true: " << ( true || true ) << "\n\n";

23   

24     // create truth table for ! (logical negation) operator

25     cout << "Logical NOT (!)"

26        << "\n!false: " << ( !false )

27        << "\n!true: " << ( !true ) << endl;

28     return 0; // indicate successful termination

29  } // end main

 

شكل 18-5   | عملگرهاي منطقي .

 

 

تقدم و شركت‌پذيري عملگرها

جدول به نمايش درآمده در شكل 19-5 تقدم عملگرهاي معرفي شده تا بدين جا را نشان مي‌دهد. تقدم عملگرها از بالا به پايين و به ترتيب كاهش مي‌يابد.

 

عملگر

شركت‌پذيري

نوع

()

++ -- static_cast<type>()

left to right

right to left

parentheses

unary postfix

++  --  +  -  ! 

right to left

unary prefix

*  /  %

left to right

multiplicative

+  -

left to right

additive

<<  >>

left to right

insertion/extraction

<  <= >  >=

left to right

relational

== !=

left to right

equality

&&

left to right

logical AND

||

left to right

logical OR

?:

right to left

conditional

=   +=  -=  *=  /=  %=

right to left

assignment

,

left to right

comma

شكل 19-5|تقدم و شركت‌پذيري عملگرهاي معرفي شده تا بدين فصل.

 

 

9-5  اشتباه گرفتن عملگر تساوي ( == ) و عملگر تخصيص ( = )

يك نوع خطا وجود دارد كه برنامه‌نويسان ‍ C++ ، بدون در نظر گرفتن ميزان تجربه اكثراً با آن مواجه مي‌شوند و بر اين اساس يك بخش مجزا براي آن در نظر گرفته‌ شده است. خطاهايي كه بطور تصادفي با جابجا نوشتن عملگرهاي == (تساوي) و = (تخصيص) رخ مي‌دهند. اشتباهي كه انجام آن موجب بروز خطاي نحوي نمي‌شود و عباراتي كه حاوي چنين اشتباهي هستند بدرستي كامپايلر مي‌شوند و برنامه شروع بكار مي‌كند، اما در زمان اجرا نتايج اشتباهي توليد مي‌كنند و خطايي كه با آن مواجه هستيم، خطاي منطقي زمان اجرا است.

از دو نظر C++ به اين مشكل رسيدگي مي‌كند. يكي اينكه هر عبارتي كه مقدار توليد مي‌كند مي‌تواند در بخش شرط هر عبارت كنترلي بكار گرفته شود. اگر مقدار عبارت، صفر باشد با آن همانند false و اگر مقدار عبارت، غيرصفر باشد با آن همان ن د true رفتار مي‌شود. دوم اينكه عبارت تخصيصي مقدار توليد مي‌كند، يعني مقداري به متغير قرار گرفته در سمت چپ عملگر تخصيص، اختصاص مي‌يابد. براي مثال، فرض كنيد قصد نوشتن عبارت زير را داشته باشيم

      if ( payCode == 4 )

          cout<< "You get a bonus!" << endl;

اما تصادفاً بنويسيم

      if ( paycode = 4 )

          cout << "You get a bonus!" << endl;

عبارت if اول بدرستي جايزه‌اي به شخصي كه paycode آن برابر 4 است اهدا مي‌كند. عبارت if دوم (كه خطا دارد)، مبادرت به ارزيابي جمله تخصيص در شرط if با ثابت 4 مي‌كند. هر مقداري غير از صفر بعنوان يك مقدار true تفسير مي‌شود، از اينرو شرط اين عبارت if هميشه true بوده و هميشه اين شخص جايزه دريافت مي‌كند، صرفنظر از اينكه مقدار payCode آن چند باشد.

 

خطاي برنامه‌نويسي

استفاده از عملگر == با هدف تخصيص و استفاده از عملگر = با هدف تساوي، خطاي منطقي است.

اجتناب از خطا

معمولاً برنامه‌نويسان شرط‌هايي همانند x == 7 را با نام متغير در سمت چپ و مقدار ثابت در سمت راست مي‌نويسند. با برعكس نوشتن اين ترتيب به نحوي كه ثابت در سمت چپ و نام متغير در سمت راست قرار گرفته باشد     (7 == x) در صورتيكه برنامه‌نويس اشتباهاً مبادرت به نوشتن = بجاي == كند، كامپايلر متوجه موضوع شده و با آن همانند يك خطاي زمان كامپايل رفتار مي‌كند، چرا كه نمي‌توانيد مقدار يك ثابت را تغيير دهيد. با انجام اينكار مي‌توان از رخ دادن خطاهاي منطقي جلوگيري كرد.

به اسامي متغيرها، مقادير سمت چپ (lvalues) گفته مي‌شود چرا كه از آنها در سمت چپ عملگر تخصيص استفاده مي‌شود. به ثابت‌ها، مقادير سمت راست (rvalues) گفته مي‌شود، چرا كه از آنها فقط در سمت راست عملگر تخصيص استفاده مي‌شود. توجه كنيد كه lvalues را مي‌توان به عنوان rvalues استفاده كرد، اما عكس اين موضوع صادق نيست. فرض كنيد برنامه‌نويس قصد دارد مقداري را به يك متغير با يك عبارت ساده همانند عبارت زير تخصيص دهد

x = 1;

اما بنويسد

x == 1;

در اينجا هم، خطاي نحوي وجود ندارد. بجاي آن، كامپايلر بسادگي مبادرت به ارزيابي عبارت رابطه‌اي مي‌كند. اگر x معادل با 1 باشد، شرط برقرار بوده (true) و عبارت با مقدار true ارزيابي مي‌شود. اگر x معادل 1 نباشد، شرط برقرار نبوده (false) و عبارت با مقدار false ارزيابي مي‌شود. صرفنظر از مقدار عبارت، عمليات تخصيص صورت نمي‌گيرد و مقدار از دست مي‌رود. مقدار x بدون تغيير باقي مي‌ماند و مي‌تواند در زمان اجرا، خطاي منطقي توليد كند. متاسفانه روش مشخصي براي اجتناب از اين مشكل وجود ندارد.

 

اجتناب از خطا

با استفاده از يك ويرايشگر متني به بررسي تمام = هاي موجود در برنامه بپردازيد و از استفاده صحيح آن مطمئن شويد.

 

10-5  چيكده برنامه‌نويسي ساخت‌يافته

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

 عبارتهاي كنترلي C++ با استفاده از دياگرام‌هاي فعاليت بصورت خلاصه در شكل 20-5 آورده شده‌اند. دايره‌هاي كوچكي كه در تصاوير بكار رفته‌اند، نشان دهنده يك نقطه ورودي و يك نقطه خروجي براي هر عبارت هستند. اتصال جداگانه از نمادهاي دياگرام به صورت غيرقراردادي مي‌تواند ما را به طرف برنامه‌هاي غيرساخت‌يافته سوق دهد. از اينرو يك برنامه‌نويس حرفه‌اي با انتخاب و تركيب نمادها بفرمي كه محدود به عبارتهاي كنترل است و ايجاد برنامه‌هاي ساخت‌يافته به وسيله تركيب عبارتهاي كنترل در دو روش ساده مي‌تواند به يك عبارت مناسب دست يابد.

براي سادگي كار، از يك نقطه ورودي و يك نقطه خروجي در عبارتهاي كنترل استفاده مي‌شود، از اينرو فقط يك راه براي ورود و يك راه براي خروج در هر عبارت كنترل وجود خواهد داشت. اتصال متوالي اين عبارتهاي كنترل و به شكل درآوردن برنامه‌هاي ساخت‌يافته آسان‌تر است، نقطه خروجي يك عبارت كنترل مستقيماً به نقطه ورودي يك عبارت كنترل ديگر متصل مي‌شود. عبارتهاي كنترل مي‌توانند بصورت متوالي قرار گيرند (يكي پس از ديگري در يك برنامه) كه به اينحالت  عبارتكنترل پشته‌اي مي‌گوئيم. قوانين برنامه‌نويسي ساخت‌يافته امكان كنترل عبارتهايي به نوع تودرتو يا آشيانه‌اي را فراهم مي‌آورند.

شكل 20-5 | عبارتهاي انتخاب و تكرار، تك ورودي/تك خروجي در C++ .

جدول شكل 21-5 حاوي قوانيني است كه براي به شكل درآوردن صحيح برنامه‌هاي ساخت‌يافته ضروري هستند. در اين قوانين فرض بر اين است كه نماد عمل براي ارائه هر نوع عمل اجرائي شامل ورودي/خروجي است.
 

قوانين شكل‌دهي برنامه‌هاي ساخت‌يافته

1) شروع با ساده‌ترين دياگرام فعاليت (شكل 22-5)

2) هر نماد وضعيت عمل را مي‌توان با دو نماد وضعيت عمل در حالت متوالي جايگزين كرد.

3) هر وضعيت عمل را مي‌توان جايگزين هر عبارت كنترلي كرد (توالي، عبارتهاي if...else ، if ، switch ، while ، do..while ، for )

4) قوانين 2 و 3 را مي‌توان هر چند بار كه مايل هستيم و با هر ترتيبي تكرار كرد.

شكل 21-5 | قوانين برنامه‌نويسي ساخت‌يافته.

 

 

با بكار بردن قوانين 21-5 هميشه يك دياگرام فعاليت مرتب و بفرم بلوكي ايجاد مي‌شود. براي مثال نتيجه اعمال مكرر قانون دوم بر روي ساده‌ترين دياگرام فعاليت (شكل 22-5)، يك دياگرام فعاليت با تعدادي وضعيت عمل كه بفرم متوالي و پشت سرهم قرار گرفته‌اند، است(شكل 23-5). دقت كنيد كه قانون دوم يك عبارت كنترلي پشته (بر روي هم قرار گرفته) ايجاد مي‌كند، بنابراين به قانون دوم، قانون پشته‌اي گفته مي‌شود. ] نكته:خطوط عمودي خط تيره در شكل 23-5 بخشي از UML نيستند. ما از آنها براي متمايز كردن چهار دياگرام فعاليت استفاده كرده‌ايم كه نشاندهنده قانون دوم از جدول 21-5 باشند. [

قانون سوم را قانون تودرتو يا آشيانه‌اي مي‌نامند. بكار بردن قانون سوم به دفعات بر روي يك فلوچارت ساده، يك فلوچارت مرتب با عبارتهاي كنترل تودرتو را نتيجه مي‌دهد. براي مثال در شكل 26-5 مستطيل قرار گرفته در ساده‌ترين فلوچارت، در بار اول با يك عبارت دو انتخابي (if/then) جايگزين شده است. سپس مجدداً قانون سوم بر روي دو مستطيل موجود در عبارت دو انتخابي اعمال شده و هر كدام يك از اين مستطيل‌ها با يك عبارت دو انتخابي جايگزين شده است. جعبه‌هاي خط چين كه در اطراف هر عبارت دو انتخابي قرار گرفته‌اند نشان مي‌دهند كه بجاي كدام مستطيل جايگزين شده‌اند.

 

قانون چهارم يك عبارت تودرتوي بزرگتر، پيچيده‌تر و عميق‌تر ايجاد مي‌كند. دياگرامي كه با استفاده از قوانين جدول 21-5 ايجاد شود تركيبي از تمام حالات ممكنه از  دياگرام‌هاي فعاليت خواهد بود و از اينرو تمام حالات برنامه‌هاي ساخت‌يافته را خواهد داشت. زيبائي اين روش در اين است كه فقط با استفاده از هفت عبارت كنترلي ساده تك ورودي/تك خروجي ايجاد شده و اجازه تركيب آنها در دو روش ساده را فراهم مي‌آورد.

اگر قوانين جدول 21-5 بكار گرفته شوند، ايجاد يك دياگرام فعاليات غيرساخت‌يافته (همانند شكل 25- 5) غيرممكن خواهد بود. اگر مطمئن نيستيد كه يك دياگرام ساخت‌يافته است، مي‌توانيد با اعمال قوانين جدول 21-5، بصورت معكوس و ساده كردن دياگرام از اين امر مطلع شويد. اگر دياگرام قابليت تبديل به يك دياگرام فعاليت ساده را داشته باشد پس دياگراتم اصلي ساخت‌يافته است و در غير اينصورت ساخت‌يافته نمي‌باشد.

توسعه برنامه‌نويسي ساخت‌يافته به سادگي امكان‌پذير است و Bohm و Jacopini نشان دادند كه براي ايجاد برنامه‌ها فقط به سه شكل كنترلي نياز است:

·         توالي

·         انتخاب

·         تكرار

توالي حالت بديهي دارد. انتخاب، با پياده سازي يكي از سه روش زير ممكن مي‌شود:

·         عبارت if (تك انتخابي)

·         عبارت if...else (دو انتخابي)

·         عبارت switch   (چند انتخابي)

هر عبارتي كه بتوان آنرا با if...else و switch  نوشت، مي‌توان با تركيب عبارتهاي if هم انجام داد (اگر چه شايد ظاهر خوبي نداشته باشد).

 

تكرار مي‌تواند با يكي از سه روش زير پياه سازي شود:

·         عبارت while

·         عبارت do…while

·         عبارت for

هر عبارت تكراري را مي‌توان با اعمال عبارت while پياده سازي كرد (اگر چه شايد ظاهر خوبي نداشته باشد).

اگر بخواهيم از مطالب گفته شده نتيجه‌گيري نمائيم، مي‌توان گفت كنترل‌هاي مورد نياز در يك برنامه C++ مي‌توانند موارد زير باشند:

·         توالي

·         عبارت انتخاب if

·         عبارت تكرار while

اين عبارتهاي كنترل ي مي‌توانند به دو روش پشته و تودرتو با يكديگر بكار گرفته شوند كه نشان از سر راست بودن و سادگي برنامه‌نويسي ساخت‌يافته دارد.

11-5 مبحث آموزشي مهندسي نرم‌افزار: شناسايي وضعيت و فعاليت شي‌ها در سيستم ATM

در بخش 13-4 مبادرت به شناسايي تعدادي از صفات كلاس مورد نياز براي پياده‌سازي سيستم ATM و افزودن آنها به دياگرام‌ كلاس در شكل 24-4 كرديم. در اين بخش، نشان خواهيم داد كه چگونه مي‌توان اين صفات را در وضعيت (حالت) يك شي عرضه كرد. همچنين مبادرت به شناسايي چندين وضعيت كليدي خواهيم كرد كه سبب اشتغال شي‌ها مي‌شوند و در مورد تغيير وضعيت دادن شي‌ها در واكنش به انواع رويدادهاي رخ داده در سيستم بحث خواهيم كرد. همچنين در ارتباط با روند كار، يا فعاليت‌ها صحبت مي‌كنيم كه شي‌ها در سيستم ATM انجام مي‌دهند. فعاليت شي‌هاي BalanceInquiry و Withdrawal را در اين بخش معرفي مي‌كنيم، كه از جمله فعاليت‌هاي كليدي در سيستم ATM هستند (نمايش موجودي و برداشت پول).

 

 

دياگرام‌هاي وضعيت ماشين

هر شي در يك سيستم در ميان دنباله‌اي از وضعيت‌هاي مشخص شده حركت مي‌نمايد. وضعيت جاري يك شي توسط مقادير موجود در صفات شي در آن زمان مشخص مي‌شود. دياگرام وضعيتماشين (كه دياگراموضعيت ناميده مي‌شود) مدل‌كننده وضعيت‌هاي كليدي يك شي بوده و نشاندهنده شرايط محيطي است كه شي در آن شرايط تغيير وضعيت مي‌دهد. برخلاف دياگرام‌هاي كلاس كه بر روي ساختار اصلي سيستم تمركز دارند، دياگرام‌هاي وضعيت، مدل‌كننده برخي از رفتار سيستم هستند. شكل 26-5 يك دياگرام وضعيت ساده است كه برخي از وضعيت‌هاي يك شي از كلاس ATM را مدل كرده است. UML هر وضعيت را در يك دياگرام وضعيت، بصورت يك مستطيلگوشه‌گرد با نام وضعيت جاي گرفته در ميان آن به نمايش در مي‌آورد.

 

جهت فلش‌ها نشاندهنده تراكنش‌هاي صورت گرفته مابين وضعيت‌ها هستند. يك شي مي‌تواند از يك وضعيت در واكنش به رويدادهاي مختلف كه در سيستم رخ مي‌دهند به وضعيت ديگر منتقل گردد. نام يا توصيف رويدادي كه سبب تراكنش شده در كنار خطي كه متناظر با تراكنش است نوشته مي‌شود. براي مثال، شي ATM از وضعيت «كاربر تاييد نشده» به وضعيت «كاربر تاييد شده» پس از تاييد كاربر از سوي پايگاه داده تغيير مي‌يابد. از مستند نيازها بخاطر داريد كه اعتبارسنجي كاربر از سوي پايگاه داده با مقايسه شماره حساب و PIN وارد شده از سوي كاربر با اطلاعات متناظر در پايگاه داده صورت مي‌گيرد.

اگر پايگاه داده تشخيص دهد كه كاربر به درستي شماره حساب و PIN را وارد كرده است، شي ATM به وضعيت «تاييد كاربر» مي‌رود و مقدار صفت userAuthenticated خود را به true تغيير مي‌دهد. زمانيكه كاربر با انتخاب گزينه "exit" از منوي اصلي اقدام به خروج از سيستم مي‌كند، شي ATM به وضعيت «كاربر تاييد نشده است» باز مي‌گردد و شرايط براي كاربر بعدي ATM مهيا مي‌شود.

 

مهندسي نرم‌افزار

معمولاً طراحان نرم‌افزار هر حالت ممكنه را در دياگرام‌هاي وضعيت قرار نمي‌دهند و فقط سعي در عرضه دياگرام‌هاي وضعيت با اهميت بيشتر يا وضعيت‌هاي پيچيده مي‌كنند.

 

 

دياگرام‌هاي فعاليت

همانند يك دياگرام وضعيت، يك دياگرام فعالي ت مبا درت به مدل كردن رفتار سيستم مي‌كند. برخلاف دياگرام وضعيت، دياگرام فعاليت مبادرت به مدل‌سازي روند كار يك شي (توالي از رويدادها) در مدت زمان اجراي برنامه مي‌كنند. دياگرام فعاليت مدل‌كننده اعمال يك شي و ترتيب انجام كار توسط آن شي است. بخاطر داريد كه از دياگرام‌هاي فعاليت UML براي نمايش جريان كنترل عبارات كنترلي در فصل‌هاي 4 و 5 استفاده كرديم.

دياگرام فعاليت به نمايش در آمده در شكل 27-5 فعاليت‌هاي انجام گرفته در مدت زمان اجراي يك تراكنش BalanceInquiry (نمايش موجودي) را مدل كرده است. فرض كرده‌ايم كه يك شي BalanceInquiry در حال حاضر مقداردهي اوليه شده و يك شماره حساب معتبر به آن تخصيص يافته است، از اينرو شي مي‌داند كه كدام موجودي را بازيابي خواهد كرد. دياگرام شامل اعمالي است كه پس از انتخاب گزينه نمايش موجودي از سوي كاربر (از منوي اصلي) و قبل از اينكه ATM كاربر را به منوي اصلي بازگرداند، رخ مي‌دهند. شي BalanceInquiry اين اعمال را انجام نمي‌دهد، از اينرو ما آنها را در اينجا مدل نكرده‌ايم.

 

دياگرام با بازيابي موجودي در دسترس، حساب كاربر و از پايگاه‌ داده آغاز مي‌شود. سپس، BalanceInquiry كل موجودي را از حساب بازيابي مي‌كند. در پايان تراكنش، ميزان موجودي بر روي صفحه نمايش ظاهر مي‌شود. با اين عمل تراكنش كامل مي‌شود.

UML يك عمل را در يك دياگرام فعاليت بصورت وضعيت عمل شده با يك مستطيل كه گوشه‌هاي چپ و راست آن حالت انحناء به خارج دارند نشان مي‌دهد. هر وضعيت عمل حاوي يك جمله توضيح عمل است، براي مثال «دريافت ميزان موجودي در حساب كاربر از پايگاه داده»، كه مشخص مي‌كند چه عملي انجام مي‌شود. يك فلش، دو وضعيت عمل را به هم متصل كرده است و نشان‌دهنده ترتيب انجام اعمال است. دايره توپر (در بالاي شكل 27-5) نشاندهنده وضعيت اوليه و آغاز روند كار مي‌باشد. در اين دياگرام، ابتدا تراكنش مبادرت به اجراي عمل «دريافت ميزان موجودي حساب كاربر از پايگاه داده» مي‌كند. سپس تراكنش (مرحله دوم) مبادرت به بازيابي كل موجودي مي‌نمايد. در پايان تراكنش هر دو موجودي را بر روي صفحه نمايش ظاهر مي‌‌سازد. دايره توپر احاطه شده در درون يك دايره (در پايين شكل 27-5) نشاندهنده وضعيت پاياني است، انتهاي روند كار پس از اينكه شي، عمل‌هاي مدل شده را انجام داده است.

شكل 28-5 نمايشي از دياگرام فعاليت براي تراكنش Withdrawal است (برداشت پول). فرض مي‌كنيم كه به شي Withdrawal در حال حاضر يك شماره حساب معتبر تخصيص يافته است. انتخاب گزينه برداشت پول از منوي اصلي يا برگشت دادن كاربر به منوي اصلي ATM را مدل نمي‌كنيم، چرا كه اين اعمال توسط شي Wihtdrawal صورت نمي‌گيرند. ابتدا تراكنش، منوي استاندارد ميزان برداشت (شكل 17-2) و گزينه لغو تراكنش را به نمايش در مي‌آورد. سپس تراكنش وارد منوي انتخابي از سوي كاربر مي‌شود. اكنون جريان فعاليت به يك نماد تصميم‌گيري مي‌رسد. اين نقطه تعيين‌كننده عمل بعدي بر مبناي محافظ شرط مربوطه است. اگر كاربر مبادرت به لغو تراكنش كند، سيستم پيغام مناسبي به نمايش در مي‌آورد. سپس جريان لغو به يك نماد ادغام مي‌رسد، مكاني كه جريان فعاليت با ساير تراكنش‌هاي ممكنه پيوند مي‌يابد. توجه كنيد كه يك نماد ادغام مي‌تواند به هر تعداد فلش تراكنش ورودي داشته باشد، اما فقط فلش تراكنش از آن خارج مي‌شود. نماد پايين دياگرام تعيين مي‌كند كه آيا تراكنش بايد از ابتدا تكرار شود يا خير. زمانيكه كاربر تراكنش را لغو كند، شرط «پول پرداخت شده يا تراكنش لغو شده» برقرار گرديده، سپس تراكنش به وضعيت پاياني فعاليت مي‌رسد.

اگر كاربر گزينه برداشت پول را از منو انتخاب كند، تراكنش مبادرت به تنظيم amount (صفتي از كلاس Withdrawal كه قبلاً در شكل 24-4 مدل شده است) با ميزان پول انتخابي از سوي كاربر مي‌كند. سپس تراكنش، موجودي حساب كاربر را از پايگاه داده بدست مي‌آورد (صفت availableBalance از شي Account كاربر). سپس جريان فعاليت به يك شرط ديگر مي‌رسد. اگر ميزان درخواستي كاربر از ميزان موجودي بيشتر باشد، سيستم يك پيغام خطا در ارتباط با اين موضوع به نمايش در مي‌آورد. سپس كنترل با ساير جريان‌هاي فعاليت قبل از رسيدن به پايين‌ترين شرط موجود در دياگرام ادغام مي‌شود. شرط «پول پرداخت نشده و كاربر تراكنش را لغو نكرده است» برقرار مي‌شود و از اينرو جريان فعاليت به بالاي دياگرام برگشت داده شده و تراكنش به كاربر اعلان مي‌كند تا مقدار جديدي وارد سازد.

اگر مقدار درخواستي كمتر يا برابر ميزان موجودي كاربر باشد، تراكنش مبادرت به تست اينكه آيا پرداخت‌كننده اتوماتيك به ميزان كافي پول نقد براي انجام تقاضاي صورت گرفته دارد يا خير انجام مي‌دهد. اگر چنين نباشد، تراكنش يك پيغام خطاي مناسب به نمايش در آورده و به نماد ادغام قبل از آخرين نماد تصميم‌گيري منتقل مي‌شود. چون پولي پرداخت نشده از اينرو جريان فعاليت به ابتداي دياگرام فعاليت برگشت داده مي‌شود و تراكنش به كاربر اطلاع مي‌دهد تا مقدار جديدي وارد سازد.

اگر پول به ميزان كافي در اختيار باشد، تراكنش با پايگاه داده وارد تعامل شده و به ميزان پول درخواستي، حساب كاربر را بدهكار مي‌كند (از مقدار موجود در هر دو صفت availableBalance و totalBalance از شي Account كم مي‌شود). سپس تراكنش مبادرت به پرداخت پول مورد تقاضا كرده و به كاربر فرمان مي‌دهد تا پول را از دستگاه بردارد. سپس جريان اصلي با دو جريان خطا و جريان لغو ادغام مي‌شود. در اينحالت، پول پرداخت شده است، از اينرو جريان فعاليت به وضعيت پاياني رسيده است.

 

اولين گام‌ها را براي مدل كردن رفتار سيستم ATM و نحوه نمايش صفات در ضمن فعاليت را برداشته‌ايم. در بخش 22-6 به عمليات كلاس‌ها رسيدگي مي‌كنيم تا مدل كاملتري از رفتار سيستم ايجاد نمائيم.

 

چهارشنبه 23 تیر 1389  6:43 PM
تشکرات از این پست
jazereyearam
jazereyearam
کاربر نقره ای
تاریخ عضویت : آذر 1387 
تعداد پست ها : 1313
محل سکونت : زنجان

پاسخ به:آموزش ++C - بخش دهم

ممنون از اموزش خوب شما
 
چهارشنبه 23 تیر 1389  9:29 PM
تشکرات از این پست
دسترسی سریع به انجمن ها