قسمت قبل را با معرفي انواع بلوكهاي سازنده كه در حل مسئله نقش دارند، آغاز كرديم. با استفاده از اين بلوكهاي سازنده تكنيكهاي ايجاد برنامه را بهبود بخشيديم. در اين فصل، مبحث تئوري و قواعد علمي برنامهنويسي ساختيافته را با معرفي مابقي عبارات كنترلي
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 به عمليات كلاسها رسيدگي ميكنيم تا مدل كاملتري از رفتار سيستم ايجاد نمائيم.