برنامه نویسی ++C جلسه هفتم
- توابع بيواسطه
تابعي که به شکل بيواسطه تعريف ميشود، ظاهري شبيه به توابع معمولي دارد با اين فرق که عبارت inline در اعلان و تعريف آن قيد شده است.
مثال 16-5 تابع cube() به شکل بيواسطه
اين همان تابع cube() مثال 3-5 است:
inline int cube(int x)
{ // returns cube of x:
return x*x*x;
}
تنها تفاوت اين است كه كلمۀ كليدي inline در ابتداي عنوان تابع ذکر شده. اين عبارت به كامپايلر ميگويد كه در برنامه به جاي cube(n) کد واقعي (n)*(n)*(n) را قرار دهد.
. به برنامۀ آزمون زير نگاه کنيد:
int main()
{ // tests the cube() function:
cout << cube(4) << endl;
int x, y;
cin >> x;
y = cube(2*x-3);}
اين برنامه هنگام کامپايل به شکل زير درميآيد، گويي اصلا تابعي وجود نداشته:
int main()
{ // tests the cube() function:
cout << (4) * (4) * (4) << endl;
int x, y;
cin >> x;
y = (2*x-3) * (2*x-3) * (2*x-3);}
احتياط:
استفاده از توابع بيواسطه ميتواند اثرات منفي داشته باشد. مثلا اگر يک تابع بيواسطه داراي 40 خط کد باشد و اين تابع در 26 نقطه مختلف از برنامۀ اصلي فراخواني شود، هنگام کامپايل بيش از هزار خط کد به برنامۀ اصلي افزوده ميشود. همچنين تابع بيواسطه ميتواند قابليت انتقال برنامۀ شما را روي سيستمهاي مختلف کاهش دهد.
وقتي كامپايلر کد واقعي تابع را جايگزين فراخواني آن ميکند، ميگوييم که تابع بيواسطه، باز ميشود.
14- چندشکلي توابع
در C++ ميتوانيم چند تابع داشته باشيم که همگي يک نام دارند. در اين حالت ميگوييم که تابع مذکور، چندشکلي دارد. شرط اين کار آن است که فهرست پارامترهاي اين توابع با يکديگر تفاوت داشته باشد. يعني تعداد پارامترها متفاوت باشد يا دست کم يکي از پارامترهاي متناظر هم نوع نباشند.
مثال 17-5 چندشکلي تابع max()
در مثال 3-5 تابع max() را تعريف کرديم. حالا توابع ديگري با همان نام ولي شکلي متفاوت تعريف ميکنيم و همه را در يک برنامه به کار ميگيريم:
int max(int, int);
int max(int, int, int);
int max(double, double);
int main()
{ cout << max(99,77) << " " << max(55,66,33) << " " << max(44.4,88.8);
}
int max(int x, int y)
{ // returns the maximum of the two given integers:
return (x > y ? x : y);
}
int max(int x, int y, int z)
{ // returns the maximum of the three given integers:
int m = (x > y ? x : y); // m = max(x , y)
return ( z > m ? z : m);
}
int max(double x, double y)
{ // return the maximum of the two given doubles:
return (x>y ? x : y);
}
در اين برنامه سه تابع با نام max() تعريف شده است.
وقتي تابع max() در جايي از برنامه فراخواني ميشود، کامپايلر فهرست آرگومان آن را بررسي ميکند تا بفهمد که کدام نسخه از max بايد احضار شود.
مثلا در اولين فراخواني تابع max() دو آرگومان int ارسال شده، پس نسخهاي که دو پارامتر int در فهرست پارامترهايش دارد فراخواني ميشود. اگر اين نسخه وجود نداشته باشد، کامپايلر intها را به double ارتقا ميدهد و سپس نسخهاي که دو پارامتر double دارد را فرا ميخواند.
14- تابع main()
برنامههايي که تا کنون نوشتيم همه داراي تابعي به نام main() هستند.
منطق C++ اين طور است که هر برنامه بايد داراي تابعي به نام main() باشد.
در حقيقت هر برنامه کامل، از يک تابع main() به همراه توابع ديگر تشکيل شده است که هر يک از اين توابع به شکل مستقيم يا غير مستقيم از درون تابع main() فراخواني ميشوند.
خود برنامه با فراخواني تابع main() شروع ميشود.
چون اين تابع يک نوع بازگشتي int دارد، منطقي است که بلوک تابع main() شامل دستور return 0; باشد هرچند که در برخي از کامپايلرهاي C++ اين خط اجباري نيست و ميتوان آن را ذکر نکرد.
مقدار صحيحي که با دستور return به سيستم عامل برميگردد بايد تعداد خطاها را شمارش کند. مقدار پيشفرض آن 0 است به اين معنا که برنامه بدون خطا پايان گرفته است.
با استفاده از دستور return ميتوانيم برنامه را به طور غيرمعمول خاتمه دهيم.
مثال 18-5 استفاده از دستور return براي پايان دادن به يك برنامه
int main()
{ // prints the quotient of two input integers:
int n, d;
cout << "Enter two integers: ";
cin >> n >> d;
if (d = = 0) return 0;
cout << n << "/" << d << " = " << n/d << endl;
}
خروجی :
Enter two integers: 99 17
99/17 = 5
دستور return تابع فعلي را خاتمه ميدهد و کنترل را به فراخواننده بازميگرداند. به همين دليل است که اجراي دستور return در تابع main() کل برنامه را خاتمه ميدهد.
چهار روش وجود دارد که بتوانيم برنامه را به شکل غيرمعمول (يعني قبل از اين که اجرا به پايان بلوک اصلي برسد) خاتمه دهيم:
1 - استفاده از دستور return
2 - فراخواني تابع exit()
(اين تابع در سرفايل
3 - فراخواني تابع abort()
4 – ايجاد يک حالت استثنا
مثال 19-5 استفاده از تابع exit() براي پايان دادن به برنامه
#include
#include
using namespace std;
double reciprocal(double x);
int main()
{ double x;
cin >> x;
cout << reciprocal(x);
}
double reciprocal(double x)1 – Exception
{ // returns the reciprocal of x:
if (x = = 0) exit(1); // terminate the program
return 1.0/x; }
دراين برنامۀ اگر كاربر عدد 0 را وارد کند، تابع reciprocal() خاتمه مييابد و برنامه بدون هيچ مقدار چاپي به پايان ميرسد
15- آرگومانهاي پيشفرض
در C++ ميتوان تعداد آرگومانهاي يک تابع را در زمان اجرا به دلخواه تغيير داد.
اين امر با استفاده از آرگومانهاي اختياري و مقادير پيشفرض امکانپذير است.
براي اين که به يک پارامتر مقدار پيشفرض بدهيم بايد آن مقدار را در فهرست پارامترهاي تابع و جلوي پارامتر مربوطه به همراه علامت مساوي درج کنيم. به اين ترتيب اگر هنگام فراخواني تابع، آن آرگومان را ذکر نکنيم، مقدار پيشفرض آن در محاسبات تابع استفاده ميشود. به همين خاطر به اين گونه آرگومانها، آرگومان اختياري ميگويند.
مثال 20-5 آرگومانهاي پيشفرض
برنامۀ زير حاصل چند جملهاي درجه سوم را پيدا ميکند. براي محاسبۀ اين مقدار از الگوريتم هورنر استفاده شده. به اين شکل که براي کارايي بيشتر، محاسبه به صورت a0 + (a1+(a2+a3x)x)x دستهبندي ميشود:
double p(double, double, double=0, double=0, double=0);
int main()
{ // tests the p() function:
double x = 2.0003;
cout << "p(x,7) = " << p(x,7) << endl;
cout << "p(x,7,6) = " << p(x,7,6) << endl;
cout << "p(x,7,6,5) = " << p(x,7,6,5) << endl;
cout << "p(x,7,6,5,4) = " << p(x,7,6,5,4) << endl;
}
double p(double x, double a0, double a1=0, double a2=0, double a3=0)
{ // returns a0 + a1*x + a2*x^2 + a3*x^3:
return a0 + (a1 + (a2 + a3*x)*x)*x;
}
خروجی :
p(x,7) = 7
p(x,7,6) = 19.0018
p(x,7,6,5) = 39.00781 – Default
p(x,7,6,5,4) = 71.0222
دقت کنيد که پارامترهايي که مقدار پيشفرض دارند بايد در فهرست پارامترهاي تابع بعد از همۀ پارامترهاي اجباري قيد شوند مثل:
void f( int a, int b, int c=4, int d=7, int e=3); // OK
void g(int a, int b=2, int c=4, int d, int e=3); // ERROR
همچنين هنگام فراخواني تابع، آرگومانهاي ذکر شده به ترتيب از چپ به راست تخصيص مييابند و پارامترهاي بعدي با مقدار پيشفرض پر ميشوند.
مثلا در تابع p() که در بالا قيد شد، فراخواني p(8.0,7,6) باعث ميشود که پارامتر x مقدار 8.0 را بگيرد سپس پارامتر a0 مقدار 7 را بگيرد و سپس پارامتر a1 مقدار 6 را بگيرد. پارامترهاي a2 و a3 مقدار پيشفرضشان را خواهند داشت. اين ترتيب را نميتوانيم به هم بزنيم. مثلا نميتوانيم تابع را طوري فرا بخوانيم که پارامترهاي x و a0 و a3 مستقيما مقدار بگيرند ولي پارامترهاي a1 و a2 مقدار پيشفرضشان را داشته باشند.
«آرايهها»
در برنامههايي که دادههاي فراواني را پردازش ميکنند استفاده از متغيرهاي معمولي کار عاقلانهاي نيست زيرا در بسياري از اين برنامهها «پردازش دستهاي» صورت ميگيرد به اين معني که مجموعهاي از دادههاي مرتبط با هم در حافظه قرار داده ميشود و پس از پردازش، کل اين مجموعه از حافظه خارج ميشود و مجموعۀ بعدي در حافظه بارگذاري ميشود. اگر قرار باشد براي اين کار از متغيرهاي معمولي استفاده شود بيشتر وقت برنامهنويس صرف پر و خالي کردن انبوهي از متغيرها ميشود. به همين دليل در بيشتر زبانهاي برنامهنويسي «آرايهها» تدارک ديده شدهاند.
آرايه را ميتوان متغيري تصور کرد که يک نام دارد ولي چندين مقدار را به طور همزمان نگهداري مينمايد.
يک آرايه، يك زنجيره از متغيرهايي است كه همه از يك نوع هستند.
به اين متغيرها «اعضاي آرايه» ميگويند.
هر عضو آرايه با يک شماره مشخص ميشود که به اين شماره «ايندکس» يا «زيرنويس» ميگويند
عناصر يک آرايه در خانههاي پشت سر هم در حافظه ذخيره ميشوند. به اين ترتيب آرايه را ميتوان بخشي از حافظه تصور کرد که اين بخش خود به قسمتهاي مساوي تقسيم شده و هر قسمت به يک عنصر تعلق دارد.
شکل مقابل آرايۀ a که پنج عنصر دارد را نشان ميدهد.
عنصر a[0] حاوي مقدار 17.5 و عنصر a[1] حاوي 19.0 و عنصر a[4] حاوي مقدار 18.0 است.
2- پردازش آرايهها
مثال 1-6 دستيابي مستقيم به عناصر آرايه
برنامۀ سادۀ زير يک آرايۀ سه عنصري را تعريف ميکند و سپس مقاديري را در آن قرار داده و سرانجام اين مقادير را چاپ ميکند:
int main()
{ int a[3];
a[2] = 55;
a[0] = 11;
a[1] = 33;
cout << "a[0] = " << a[0] << endl;
cout << "a[1] = " << a[1] << andl;
cout << "a[2] = " << a[2] << endl;
}
خروجی:
a[0] = 11
a[1] = 33
a[2] = 55
نحو کلي براي اعلان آرايه به شکل زير است:
type array_name[array_size];
عبارت type نوع عناصر آرايه را مشخص ميکند. array_name نام آرايه است .
array_size تعداد عناصر آرايه را نشان ميدهد. اين مقدار بايد يک عدد ثابت صحيح باشد و حتما بايد داخل کروشه [] قرار بگيرد.
3- مقداردهي آرايهها
در C++ ميتوانيم يک آرايه را با استفاده از فهرست مقداردهي، اعلان و مقدارگذاري کنيم:
float a[] = {22.2,44.4,66.6};
به اين ترتيب مقادير داخل فهرست به همان ترتيبي که چيده شدهاند درون عناصر آرايه قرار ميگيرند. اندازه آرايه نيز برابر با تعداد عناصر موجود در فهرست خواهد بود.
پس همين خط مختصر، آرايهاي از نوع float و با نام a و با تعداد سه عنصر اعلان کرده و هر سه عنصر را با مقدارهاي درون فهرست، مقداردهي ميکند.
مثال 3-6 مقداردهي آرايه با استفاده از فهرست مقداردهي
برنامۀ زير، آرايۀ a را مقداردهي کرده و سپس مقدار هر عنصر را چاپ ميكند:
int main()
{ float a[] = { 22.2, 44.4, 66.6 };
int size = sizeof(a)/sizeof(float);
for (int i=0; i
cout << "\ta[" << i << "] = " << a[i] << endl;
}
خروجی :
a[0] = 22.2
a[1] = 44.4
a[2] = 66.6
هنگام استفاده از فهرست مقداردهي براي اعلان آرايه، ميتوانيم تعداد عناصر آرايه را هم به طور صريح ذکر کنيم. در اين صورت اگر تعداد عناصر ذکر شده از تعداد عناصر موجود در فهرست مقداردهي بيشتر باشد، خانههاي بعدي با مقدار صفر پر ميشوند:
float a[7] = { 55.5, 66.6, 77.7 };
دقت کنيد که تعداد مقادير موجود در فهرست مقداردهي نبايد از تعداد عناصر آرايه بيشتر باشد:
float a[3] = { 22.2, 44.4, 66.6, 88.8 }; // ERROR: too many values!
يك آرايه را ميتوانيم به طور کامل با صفر مقداردهي اوليه کنيم. براي مثال سه اعلان زير با هم برابرند:
float a[ ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
float a[9] = { 0, 0 };
float a[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
اما مطلب فوق اصلا به اين معني نيست که از فهرست مقداردهي استفاده نشود.
درست مثل يک متغير معمولي، اگر يک آرايه مقداردهي اوليه نشود، عناصر آن حاوي مقادير زباله خواهد بود.
مثال 5-6 يك آرايۀ مقداردهي نشده
برنامۀ زير، آرايۀ a را اعلان ميکند ولي مقداردهي نميكند. با وجود اين، مقادير موجود در آن را چاپ ميكند:
int main()
{ const int SIZE=4; // defines the size N for 4 elements
float a[SIZE]; // declares the array's elements as float
for (int i=0; i
cout << "\ta[" << i << "] = " << a[i] << endl;
}
خروجی :
a[0] = 6.01838e-39
a[1] = 9.36651e-39
a[2] = 6.00363e-39
a[3] = 0
آرايهها را ميتوان با استفاده از عملگر جايگزيني مقداردهي کرد اما نميتوان مقدار آنها را به يکديگر تخصيص داد:
float a[7] = { 22.2, 44.4, 66.6 };
float b[7] = { 33.3, 55.5, 77.7 };
b = a; // ERROR: arrays cannot be assigned!
همچنين نميتوانيم يك آرايه را به طور مستقيم براي مقداردهي به آرايۀ ديگر استفاده كنيم:
float a[7] = { 22.2, 44.4, 66.6 };
float b[7] = a; // ERROR: arrays cannot be used as nitializers!
4- ايندكس بيرون از حدود آرايه
در بعضي از زبانهاي برنامهنويسي، ايندکس آرايه نميتواند از محدودۀ تعريف شده براي آن بيشتر باشد. براي مثال در پاسکال اگر آرايۀ a با تعداد پنج عنصر تعريف شده باشد و آنگاه a[7] دستيابي شود، برنامه از کار ميافتد. اين سيستم حفاظتي در C++ وجود ندارد. مثال بعدي نشان ميدهد که ايندکس يک آرايه هنگام دستيابي ميتواند بيشتر از عناصر تعريف شده براي آن باشد و باز هم بدون اين که خطايي گرفته شود، برنامه ادامه يابد.
مثال 6-6 تجاوز ايندکس آرايه از محدودۀ تعريف شده براي آن
برنامۀ زير يک خطاي زمان اجرا دارد؛ به بخشي از حافظه دستيابي ميکند که از محدودۀ آرايه بيرون است:
in main()
{ const int SIZE=4;
float a[SIZE} = { 33.3, 44.4, 55.5, 66.6 };
for (int i=0; i<7; i++) //ERROR: index is out of bounds!
cout << "\ta[" << i << "] = " << a[i] << endl;
}
آرايهاي که در اين برنامه تعريف شده، چهار عنصر دارد ولي تلاش ميشود به هفت عنصر دستيابي شود. سه مقدار آخر واقعا جزو آرايه نيستند و فقط سلولهايي از حافظهاند که دقيقا بعد از عنصر چهارم آرايه قرار گرفتهاند. اين سلولها داراي مقدار زباله هستند.
خروجی :
a[0] = 33.3
a[1] = 44.4
a[2] = 55.5
a[3] = 66.6
a[4] = 5.60519e-45
a[5] = 6.01888e-39
a[6] = 6.01889e-39
* مثال 7-6 اثر همسايگي
برنامۀ زير از ايندکس خارج از محدوده استفاده ميکند و اين باعث ميشود که مقدار يک متغير به طور ناخواسته تغيير کند:
int main()
{ const int SIZE=4;
float a[] = { 22.2, 44.4, 66.6 };
float x=11.1;
cout << "x = " << x << endl;
a[3] = 88.8; // ERROR: index is out of bounds!
cout << "x = " << x << endl;
}
خروجی:
x = 88.8
متغير x بعد از آرايۀ a اعلان شده، پس يک سلول چهاربايتي بلافاصله بعد از دوازده بايت آرايه به آن تخصيص مييابد. بنابراين وقتي برنامه تلاش ميکند مقدار 88.8 را در a[3] قرار دهد (که جزو آرايه نيست) اين مقدار به شکل ناخواسته در x قرار ميگيرد. شکل مقابل نشان ميدهد چطور اين اتفاق در حافظه رخ ميدهد.
مثال بعدي نوع ديگري از خطاي زمان اجرا را نشان ميدهد: وقتي ايندکس آرايه بيش از حد بزرگ باشد.
اين خطا يکي از وحشتناکترين خطاهاي زمان اجراست زيرا ممکن است اصلا نتوانيم منبع خطا را کشف کنيم. حتي ممکن است به اين روش دادههاي برنامههاي ديگري که در حال کارند را خراب کنيم و اين باعث ايجاد اختلال در کل سيستم شود. به اين خطا «اثر همسايگي» ميگويند. اين وظيفۀ برنامهنويس است که تضمين کند ايندکس آرايه هيچگاه از محدودۀ آن خارج نشود.
مثال 8-6 ايجاد استثناي مديريت نشده
برنامۀ زير از كار ميافتد زيرا ايندكس آرايه خيلي بزرگ است:
int main()
{ const int SIZE=4;
float a[] = { 22.2, 44.4, 66.6 };
float x=11.1;
cout << "x = " << x << endl;
a[3333] =88.8;//ERROR: index is out of bounds!
cout << "x = " << x << endl;
}
وقتي اين برنامه روي رايانهاي با سيستم عامل ويندوز اجرا شود، يک صفحۀ هشدار که در شکل نشان داده شده روي صفحه ظاهر ميشود.
اين پنجره بيان ميکند که برنامه تلاش دارد به نشاني 0040108e از حافظه دستيابي کند. اين مکان خارج از حافظۀ تخصيصي است که براي اين برنامه منظور شده، بنابراين سيستم عامل برنامه را متوقف ميکند.
پردازشگر استثنا
خطايي که در مثال 8-6 بيان شده يک «استثناي مديريت نشده» ناميده ميشود زيرا کدي وجود ندارد که به اين استثنا پاسخ دهد.
در C++ ميتوانيم کدهايي به برنامه اضافه کنيم که هنگام رخ دادن حالتهاي استثنا، از توقف برنامه جلوگيري کند. به اين کدها «پردازشگر استثنا» ميگويند.




