Professional Documents
Culture Documents
6الــــــــــدوال
Chapter 6. Functions
الـمحتويات
Contents
اسما.
ً الدالة عبارة عن كتلة من أكواد تحمل
نقوم بتنفيذ تلك األكواد عن طريق استدعاء الدالة. •
قد تأخذ الدالة صفرًا أو أكرث من الوسيطات (عاد ًة) ينتج عنها نتيجة. •
يمكن زيادة تعبئة الدوال ،ما يعين إمكانية أن يشري نفس االسم إلى عدة دوال مختلفة. •
6٫1أســــــــــاسيات الــــــدالــــــة
6.1. Function Basics
يتكون تعريف الدالة عاد ًة من نوع إعادة ،واسم ،وقائمة صفرية أو أكرث من املعلمات ،وبنية.
يتم تحديد املعلمات في قائمة مفصولة بفواصل محاطة بأقواس. •
ويتم تحديد اإلجراءات اليت تؤديها الدالة في كتلة جمل ،يشار إليها باسم بنية الدالة. •
نقوم بتنفيذ دالة من خالل عامل االستدعاء ،وهو زوج من األقواس. •
يأخذ عامل اﻻستدعاء تعبريًا عبارة عن دالة أو يشري إلى دالة. •
يوجد داخل األقواس قائمة وسيطات مفصولة بفواصل .ويتم استخدام تلك الوسيطات لتهيئة معلمات الدالة. •
نوع تعبري االستدعاء هو نوع إعادة الدالة. •
كمثال ،
سنكتب دالة لتقرير املرضوب لرقم معني. •
املرضوب للرقم nهو حاصل رضب األرقام من 1إلى .nمرضوب - 5مثالً -هو .120 •
1 * 2 * 3 * 4 * 5 = 120
)(int main
{
;)int j = fact(5 )// j equals 120, i.e., result of fact(5
;cout << "5! is " << j << endl
;return 0
}
فشل أول استدعاء لعدم وجود تحويل من حرفي ثابت إلى عدد صحيح. •
مررت االستدعاءات الثانية والثالثة عد ًدا خاطًئ ا من الوسيطات. •
وﻷنه يجب استدعاء دالة factبوسيطة واحدة ؛ •
فمن الخطأ استدعاءها بأي عدد آخر. •
اﻻستدعاء األخري رشعي ألنه يوجد تحويل من املزدوج العرشي إلى عدد صحيح. •
ضمنيا إلى عدد صحيح (من خالل االقتطاع).
ً في هذا االستدعاء ،يتم تحويل الوسيطة •
بعد التحويل ،سيعادل هذا اﻻستدعاء •
;)fact(3
تتكون قائمة املعلمات عاد ًة من قائمة معلمات مفصولة بفواصل ، •
بـمع ِلن واحد.
كل منها يبدو كإعالن ُ •
فحىت في حالة تطابق نوعي املعلمتني ،يجب تكرار كتابة النوع : •
ال يمكن أن تحمل معلمتان االسم نفسه .عالوة على ذلك ، •
ال يسمح أن تستخدم متغريات محلية في النطاق الخارجي للدالة نفس االسم مثل أي معلمة. •
أسماء املعلمات اختيارية .مع ذلك ،ال توجد طريقة الستخدام معلمة غري مسماة.
لذلك ،عاد ًة ما يكون للمعلمات أسماء. •
ومن حني آلخر ،تحتوي الدالة على معلمة غري مستخدمة. •
غالبا ما تُرتك مثل هذه املعلمات بدون تسمية ،لإلشارة إلى عدم استخدامها.
ً •
ال يؤدي ترك املعلمة بدون اسم إلى تغيري عدد الوسيطات اليت يجب أن يوفرها اﻻستدعاء. •
يجب أن يوفر االستدعاء وسيطة لكل معلمة ،حىت إن لم يتم استخدام تلك املعلمة. •
التمرين :6.2
أيا من الدوال التالية بها أخطاء وملاذا.
-حدد ً
-اقرتح كيف يمكنك تصحيح املشاكل.
{ )((a) int f
;string s
// ...
;return s
}
} (b) f2(int i) { /* ... */
} (c) int calc(int v1, int v1) /* ... */
;(d) double square(double x) return x * x
التمرين :6.3
-اكتب واخترب نسختك الخاصة من .fact
التمرين :6.4
رقما وتولد مرضوب هذا الرقم.
-اكتب دالة تتفاعل مع املستخدم ،تطلب ً
-قم باستدعاء تلك الدالة من .main
التمرين :6.5
اكتب دالة إلعادة القيمة املطلقة absolute valueلوسيطتها.
في لغة يس ، ++تملك األسماء نطاقاً ،وتملك الكائنات ُع ْمراً .من املهم فهم هذين املفهومني.
فنطاق االسم هو جزء من بنية الربنامج يظهر فيه هذا االسم. •
أما ُع ْم ر الكائن فهو الوقت الذي يوجد فيه ذلك الكائن أثناء تنفيذ الربنامج. •
يتم خلق الكائنات اليت تتوافق مع املتغريات املحلية العادية عندما يمر مسار تحكم الدالة عرب تعريف املتغري.
حيث يتم تدمريها عندما يمر عنرص التحكم عرب نهاية الكتلة اليت تم تعريف املتغري فيها. •
تُعرف الكائنات اليت تتواجد فقط أثناء تنفيذ كتلة بالكائنات التلقائية .Automatic Objects •
معرفة.
بعد أن يخرج التنفيذ من كتلة ،تكون قيم الكائنات التلقائية اليت أنشئت في تلك الكتلة غري ّ •
تتم تهيئة الكائنات التلقائية املقابلة ملعلمات الدالة على الوسيطات اليت يتم تمريرها إلى الدالة.
يتم تهيئة الكائنات التلقائية املقابلة على املتغريات املحلية إذا كان تعريفها يحتوي على ُمهئي. •
عدا ذلك ،سيتم تهيئتها بشكل افرتايض ،ما يعين أن املتغريات املحلية غري املهيأة من النوع املدمج لها قيم •
معرفة.
غري ّ
قد يكون من املفيد أن يكون لديك متغري محلي تستمر حياته خالل استدعاءات الدالة.
نحصل على مثل هذه الكائنات عن طريق تعريف املتغري املحلي كـساكن .static •
تتم تهيئة كل كائن ساكن محلي قبل أن يمر التنفيذ ألول مرة عرب تعريف الكائن. •
ال يتم إتالف الساكن املحلي عند إنهاء الدالة ؛ بل يتم تدمريه عند إنهاء الربنامج. •
{ )(size_t count_calls
;static size_t ctr = 0 // value will persist across calls
} ;return ++ctr
{ )(int main
)for (size_t i = 0; i != 10; ++i
;cout << count_calls() << endl
} ;return 0
مثل أي اسم آخر ،يجب اإلعالن عن اسم الدالة قبل أن نتمكن من استخدامها.
وكما هو حال املتغريات ،يمكن تعريف الدالة مرة واحدة فقط ولكن يمكن اإلعالن عنها عدة مرات. •
باستثناء واحد سنقوم بتغطيته في الفقرة ، 15.3 •
يمكننا أن نعلن عن دالة لم يتم تعريفها طاملا أننا ال نستخدم تلك الدالة أب ًدا. •
تماما مثل تعريف الدالة باستثناء أن اإلعالن ليس له بنية دالة.
ً إعالن الدالة هو •
في اﻹعالن ،تحل الفاصلة املنقوطة محل بنية الدالة. •
ونظرًا لعدم وجود بنية إلعالن الدالة ،فال داعي ألسماء املعلمات. •
غالبا ما يتم حذف أسماء املعلمات في اإلعالن.
ً وبالتالي ، •
لكن بالرغم من أن أسماء املعلمات غري مطلوبة ،إال أنه يمكن استخدامها ملساعدة مستخدمي الدالة على فهم •
ما تفعله الدالة :
// parameter names chosen to indicate that iterators denote range of
values to print
void print(vector<int>::const_iterator beg,
;)vector<int>::const_iterator end
هذه العنارص الثالثة -نوع اإلعادة ،واسم الدالة ،وأنواع املعلمات -تصف واجهة الدالة. •
جميعها تحدد املعلومات اليت نحتاجها الستدعاء الدالة. •
أيضا باسم النموذج األولي للدالة .the function prototype
تُعرف إعالنات الدوال ً •
أفضل املمارسات
نظرًا ألن برامجنا تزداد تعقي ًدا ،فسرنغب في تخزين أجزاء الربنامج املختلفة في ملفات منفصلة.
• مثالً ،قد نقوم بتخزين الدوال اليت كتبناها للتمارين الواردة في الفقرة 6.1في ملف واحد وتخزين الكود
الذي يستخدم هذه الدوال في ملفات مصدر أخرى.
عموما بالتجميع املنفصل.
ً • للسماح لكتابة الربامج بأجزاء منطقية ،تدعم لغة يس ++ما يُعرف
يتيح لنا التجميع املنفصل تقسيم برامجنا إلى عدة ملفات ،يمكن تجميع كل منها بشكل مستقل. •
كمثال ،
افرتض أن تعريف دالة factفي ملف يسمى ، fact.cc •
وأن اإلعالن عنها موجود في ملف عنوان يسمى .Chapter6.h •
سيحتوي ملفنا - fact.ccمثل أي ملف يستخدم تلك الدوال -على عنوان .Chapter6.h •
سنخزن دالة mainتستدعي factفي ملف ثانٍ باسم .factMain.cc •
إلنتاج ملف قابل للتنفيذ ،يجب أن نخرب املرتجم بمكان العثور على كل كود نستخدمه. •
قد نقوم بتجميع هذه امللفات على النحو التالي: •
ستحتاج إلى مراجعة دليل مستخدم املرتجم لفهم كيفية تجميع وتنفيذ الربامج املكونة من ملفات مصدر متعددة.
كما رأينا ،في كل مرة نستدعي دالة ،يتم إنشاء معلماتها وتهيئتها على الوسيطات اليت تم تمريرها في االستدعاء.
ملحوظة
كما هو حال أي متغري آخر ،يقرر نوع املعلمة التفاعل بني املعلمة ووسيطتها.
مرجعا ،إذن سرتتبط املعلمة بوسيطتها .عدا ذلك ،يتم نسخ قيمة الوسيطة.
ً فإن كانت املعلمة •
مرجعا ،
ً عندما تكون املعلمة
فإننا نقول إن وسيطتها املقابلة "مررت بواسطة مرجع “"”passed by reference •
أو أن الدالة "استدعيت بواسطة مرجع “."”.called by reference •
مستعارا للكائن املرتبط بها ؛
ً اسما
ً وكما هو حال أي مرجع آخر ،تعترب املعلمة املرجعية •
أي أن املعلمة هي اسم مستعار لوسيطتها املقابلة. •
أما عند نسخ قيمة الوسيطة ،تكون املعلمة والوسيطة كائنني مستقلني.
نقول إن مثل هذه الوسيطات "مررت بواسطة قيمة “"”passed by value •
أو أن الدالة "استدعيت بواسطة قيمة “."”.called by value •
عندما نقوم بتهيئة متغري نوع غري مرجعي ،يتم نسخ قيمة املُهئي.
فال تؤثر التغيريات اليت تم إجراؤها على املتغري على املُهئي :
تماما ؛
ً تمرير الوسيطة بواسطة قيمة يعمل بنفس الطريقة
فال يشء تفعله الدالة للمعامل يمكن أن يؤثر على الوسيطة .مثالً ،داخل factيتم تناقص قيمة املعلمة : val
وبالرغم من أن factتغري قيمة ، valإﻻ أن هذا التغيري ليس له أي تأثري على الوسيطة املمررة إلى .fact
فاستدعاء ) fact(iلن يغري قيمة .i
;int n = 0, i = 42
;int *p = &n, *q = &i // p points to n; q points to i
;*p = 42 // value in n is changed; p is unchanged
;p = q // p now points to i; values in i and n unchanged
// function that takes pointer and sets pointed-to value to zero
{ )void reset(int *ip
;*ip = 0 // changes value of object to which ip points
;ip = 0 } // changes only local copy of ip; argument is unchanged
بعد استدعاء ، resetسيكون الكائن الذي تشري إليه الوسيطة ، 0لكن وسيطة املؤرش نفسها لم تتغري:
أفضل املمارسات
غالبا ما يستخدم مربمجو لغة Cمعلمات املؤرش للوصول إلى كائنات خارج الدالة.
ً
أما في يس ، ++فيستخدم املربمجون بشكل عام معلمات املرجع بدال ً من ذلك.
تذكر أن العمليات على مرجع هي في الواقع عمليات على كائن يشري إليه املرجع :
;int n = 0, i = 42
;int &r = n )// r is bound to n (i.e., r is another name for n
;r = 42 // n is now 42
;r = i // n now has same value as i
;i = r // i has the same value as n
تستغل معلمات املرجع هذا السلوك. •
فغالبًا ما يتم استخدامها لتسمح لدالة بتغيري قيمة واحدة أو أكرث من وسيطاتها. •
كمثال ،يمكننا إعادة كتابة برنامج resetمن القسم السابق لتأخذ مرجع بدال ً من مؤرش : •
// function that takes reference to int and sets given object to zero
)void reset(int &i // i is another name for object passed to reset
{
;i = 0 // changes value of object to which i refers
}
كما هو حال أي مرجع آخر ،ترتبط معلمة املرجع مبارش ًة بالكائن الذي يتم تهيئتها عليه. •
فعندما نستدعي هذا اإلصدار من ، resetسيكون مربوطاً بأي كائن intنمرره. •
وكما هو حال أي مرجع ،يتم إجراء التغيريات على الكائن الذي يشري إليه. •
في هذه الحالة ،هذا الكائن هو وسيطة .reset •
عندما نستدعي هذا اإلصدار من ، resetفإننا نمرر كائنًا مبارشة ؛ ليست هناك حاجة لتمرير عنوانه:
;int j = 42
;)reset(j // j is passed by reference; value in j is changed
;cout << "j = " << j << endl // prints j = 0
في هذا االستدعاء ،املعامل iهو مجرد اسم آخر لـ .j •
أي استخدام لـ iداخل resetهو استخدام لـ .j •
كما سرنى في الفقرة ، 3.2.6ينبغي أن تستخدم الدوال مراجع لثابت ملعلمات مرجعية ال تحتاج إلى تغيريها.
أفضل املمارسات
ينبغي على معلمات مرجع ﻻ يتم تغيريها داخل دالة أن تشري لثابت.
لكن أسهل حل هو تمرير وسيطة مرجع إضافية تحتفظ بعدد مرات التكرار :
بعد اﻻستدعاء ،ستكون قيمة ctrهي عدد مرات حدوث ، o •
وسوف تشري indexﻷول تكرار إن وجد. •
مساويا لـ () s.sizeوسيساوي ctrصفرًا.
ً عدا ذلك ،سيكون index •
التمرين :6.14
مرجعيا.
ً نوعا
-قدم مثاال ً يوضح مىت يجب أن تكون املعلمة ً
مرجعا.
ً -أعط مثاال ً عندما ال يجب أن تكون املعلمة
التمرين :6.15
مرجعا لـثابت لكن occursمرجع
ً -ارشح املنطق لنوع كل من معلمات find_charلكن خصوصاً :ملاذا يُعد s
عادي؟
-لم هاتان املعلمتان مرجعيتان ،لكن املعلمة char cليست كذلك؟
مرجعا عادياً لغري ثابت؟
ً -ما الذي سيحدث لو جعلنا s
-وماذا لو جعلنا occursمرجعاً لثابت؟
عندما نستخدم معلمات ثابتة ،من املهم أن نتذكر مناقشة الثابت عالي املستوى من فقرة . 3.4.2
كما رأينا في تلك الفقرة ،فالثابت عالي املستوى هو الذي ينطبق على الكائن نفسه :
} void fcn(const int i) { /* fcn can read but not write to i */
يمكننا أن نستدعي fcnوتمرير إما عدد صحيح ثابت أو عادي (غري ثابت).
فحقيقة أن القيم الثابتة عالية املستوى يتم تجاهلها في إحدى املعلمات لها تأثري ضمين مفاجئ :
} void fcn(const int i) { /* fcn can read but not write to i */
)void fcn(int i) { /* . . . */ } // error: redefines fcn(int
ففي يس ، ++يمكننا تعريف عدة دوال مختلفة تملك نفس االسم.
مع ذلك ،ال يمكننا القيام بذلك إال إن كانت قوائم معلماتها مختلفة بما فيه الكفاية. •
تماما إلى أي إصدار من .fcn
ً ونظر ًا لتجاهل الثوابت عالية املستوى ،يمكننا تمرير نفس األنواع •
سيكون إصدار fcnالثاني خطأ .بالرغم مما يبدو عليه اﻷمر ،فإن قائمة معلماتها ال تختلف عن اﻷولى. •
وألن املعلمات تتم تهيئتها بنفس طريقة املتغريات ،فقد يكون من املفيد تذكر قواعد التهيئة العامة.
يمكننا تهيئة كائن ثابت منخفض املستوى على كائن غري ثابت ولكن ليس العكس ، •
ويجب تهيئة مرجع عادي على كائن من نفس النوع. •
;int i = 42
;const int *cp = &i // ok: but cp can't change i
;const int &r = i // ok: but r can't change i
;const int &r2 = 42 // ok
;int *p = cp // error: types of p and cp don't match
;int &r3 = r // error: types of r3 and r don't match
;int &r4 = 42 // error: can't initialize plain reference from literal
;int i = 0
;const int ci = i
;string::size_type ctr = 0
;)reset(&i //calls version of reset that has int* parameter
;)reset(&ci //error: can't initialize int* from pointer to const int object
;)reset(i //calls version of reset that has int& parameter
;)reset(ci //error: can't bind plain reference to const object ci
;)reset(42 //error: can't bind plain reference to literal
;)reset(ctr //error: types don't match; ctr has unsigned type
// ok: find_char's first parameter is reference to const
;)find_char("Hello World!", 'o', ctr
Use Reference to const When Possible استخدام مرجع لثابت عند اﻹمكان
من الخطأ الشائع إلى حد ما تعريف معلمات ال تغريها دالة مثل مرجع (عادي).
انطباعا مضلاًل بأن الدالة قد تغري قيمة وسيطتها.
ً يؤدي القيام بذلك إلى منح مستدعي الدالة •
عالوة على ذلك ،فإن استخدام مرجع بدال ً من مرجع لثابت بال داع يحد من نوع الوسيطات اليت يمكن •
استخدامها مع الدالة.
كما رأينا للتو ،ال يمكننا تمرير كائن ثابت ،أو حرفي ،أو كائن يتطلب التحويل إلى معلمة مرجع عادية. •
يمكن أن يكون تأثري هذا الخطأ واسع االنتشار بشكل مدهش. •
على سبيل املثال ،فكر في دالتنا find_charمن فقرة .2.2.6 •
مرجعا لثابت.
ً جعلت تلك الدالة (بشكل صحيح) معلمتها السلسلة •
عرفنا هذه املعلمة كسلسلة مرجع عادية & :
إن ّ •
حينها لن يمكننا استدعاء find_charإﻻ على كائن سلسلة .وسيكون استدعاء مثل
إذا أخذ find_charسلسلة مرجع عادية & ،سيكون استدعاء find_charهذا خطأ في وقت الرتجمة. •
تكمن املشكلة في أن sمرجع لسلسلة ثابتة ،لكن تم تعريف ( find_charخطًأ) لتأخذ مرجعاً عادياً. •
قد يكون من املغري محاولة إصالح هذه املشكلة عن طريق تغيري نوع املعلمة في .is_sentence •
لكن هذا اإلصالح لن يؤدي إال إلى انتشار الخطأ -يمكن ملستدعي is_sentenceتمرير سالسل غري •
ثابتة فقط.
التمرين :6.17
-اكتب دالة لتحديد ما إذا كانت سلسلة تحتوي على أحرف كبرية.
-اكتب دالة لتغيري سلسلة ما إلى األحرف الصغرية.
-هل املعلمات اليت استخدمتها في هذه الدوال تملك نفس النوع؟
-إذا كان األمر كذلك ملاذا؟ إذا لم يكن كذلك ،فلماذا؟
التمرين :6.18
-اكتب إعالنات لكل من الدوال التالية.
-عندما تكتب هذه التعريفات ،استخدم اسم الدالة لإلشارة إلى دالة الدالة.
دالة تسمى compareتقوم بإعادة منطقي ولها معلمتان عبارة عن مراجع لفئة تسمى .matrix أ-
دالة تسمى change_valتعيد مكرر متجه< >intوتأخذ معلمتني :أحدهما intواآلخر مكرر ملتجه < ب-
.>int
التمرين :6.19
-بافرتاض اإلعالنات التالية ،حدد أي اﻻستدعاءات الرشعية وأيها غري رشعية.
-بالنسبة لغري الرشعية تلك ،ارشح السبب.
;)double calc(double
;)int count(const string &, char
;)int sum(vector<int>::iterator, int
;)vector<int> vec(10
vector<int>::iterator,
;)(a) calc(23.4, 55.1
;)'(b) count("abcda", 'a
;)(c) calc(66
;)(d) sum(vec.begin(), vec.end(), 3.8
التمرين :6.20
-مىت يجب أن تكون معلمات املرجع مراجع لثابت؟
مرجعا لثابت؟
ً -ماذا يحدث إذا جعلنا معلمة مرجع عادي ًة بينما يمكن أن تكون
تملك املصفوفات مزيتان خاصة تؤثران على كيفية تعريفنا واستخدامنا للدوال اليت تعمل عليها :
اﻷولى أنه ال يمكننا نسخ مصفوفة ،والثانية أنه عندما نستخدم مصفوفة سيتم تحويلها (عاد ًة) إلى مؤرش. •
وألننا ال نستطيع نسخ مصفوفة ،فال يمكننا تمرير مصفوفة بالقيمة. •
وألنه يتم تحويل املصفوفات إلى مؤرشات ،فعندما نقوم بتمرير مصفوفة إلى دالة ، •
فإننا في الواقع نمرر مؤرشًا ألول عنرص في املصفوفة. •
وبالرغم من أننا ال نستطيع تمرير مصفوفة بالقيمة ،إﻻ أنه بإمكاننا كتابة ُمعامل يشبه مصفوفة :
بغض النظر عما يبدو ،فإن هذه اإلعالنات متكافئة : •
فكل منها يعلن عن دالة بمعامل واحد من نوع مؤرش عدد صحيح ثابت. •
عندما يتحقق املرتجم من استدعاء ، print •
فإنه يفحص فقط كون الوسيطة تحمل نوع مؤرش عدد صحيح ثابت : •
تحذير
مثل أي كود يستخدم مصفوفات ،يجب أن تضمن الدوال اليت تأخذ معلمات مصفوفة
أن تبقى جميع استخدامات املصفوفة ضمن حدودها.
وألن املصفوفات يتم تمريرها كمؤرشات ،فإن الدوال عادة ال تعرف حجم املصفوفة املعطاة لها. •
يجب أن تعتمد على املعلومات اإلضافية املقدمة من قبل املستدعي. •
هناك ثالث تقنيات شائعة تستخدم إلدارة معلمات املؤرش. •
تتطلب الطريقة األولى إلدارة وسيطات مصفوفة أن تحتوي املصفوفة نفسها على عالمة نهاية.
سالسل األحرف من نمط اليس هي مثال على هذا النهج. •
يتم تخزين سالسل نمط اليس في مصفوفات أحرف حيث يتبع آخر حرف من السلسلة حرف خال. •
تتوقف دوال تتعامل مع سالسل نمط اليس عن معالجة مصفوفة عندما ترى حر ًفا خالياً : •
الطريقة الثانية املستخدمة إلدارة وسيطات مصفوفة هي تمرير مؤرشات ﻷول عنرص وآخر عنرص في املصفوفة.
هذا النهج مستوحى من التقنيات املستخدمة في املكتبة القياسية. •
سنتعرف على املزيد حول هذا النمط من الربمجة في الجزء الثاني. •
باستخدام هذا األسلوب ،سنطبع عنارص في مصفوفة على النحو التالي: •
تستخدم whileعامل إلغاء املرجع و تزايد الالحقة لطباعة العنرص الحالي وتتقدم عنرص واحد في كل مرة •
عرب املصفوفة.
تتوقف الحلقة عندما تساوي begالنهاية .end •
والستدعاء هذه الدالة ، •
نقوم بتمرير مؤرشين -أحدهما ألول عنرص نريد طباعته واآلخر ملا بعد آخر عنرص : •
تعد هذه الدالة آمنة ،طاملا أن املستدعي يحسب املؤرشات بشكل صحيح. •
هنا ندع مكتبة دوال بداية ونهاية توفر هذه املؤرشات. •
الطريقة الثالثة لوسيطات مصفوفة -وهي الشائعة في برامج يس وبرامج يس ++األقدم – تتمثل في تحديد معلمة
ثانية تشري إلى حجم املصفوفة.
باستخدام هذا األسلوب ،سنعيد كتابة printعلى النحو التالي:
يستخدم هذا اإلصدار معلمة sizeلتحديد عدد العنارص املطلوب طباعتها. •
عندما نستدعي ، printيجب أن نمرر هذه املعلمة اإلضافية: •
يتم تنفيذ الدالة بأمان طاملا أن الحجم الذي تم تمريره ال يزيد عن حجم املصفوفة الفعلي .
الحظ أن جميع اإلصدارات الثالثة من دالتنا printحددت معلمات مصفوفتها كمؤرشات لثابت.
املناقشة الواردة في الفقرة 3.2.6تنطبق بالتساوي على املؤرشات مثلها مثل املراجع. •
عندما ال تحتاج الدالة إلى الوصول للكتابة على عنارص مصفوفة ، •
حينها يجب أن تكون معلمة املصفوفة مؤرشًا لثابت. •
عاديا لنوع غري ثابت ما لم تكن بحاجة إلى تغيري قيمة العنرص.
ً يجب أن تكون املعلمة مؤرشًا •
كما يمكننا تعريف متغري يمثل مرجعاً ملصفوفة ،يمكننا تعريف معلمة تكون مرجعاً ملصفوفة.
وكالعادة ،ستكون معلمة املرجع مربوطة بالوسيطة املقابلة ،واليت ستكون في هذه الحالة عبارة عن مصفوفة :
ملحوظة
ونظر ًا ألن حجم املصفوفة جزء من نوعها ،فمن اآلمن االعتماد على الحجم في بنية الدالة. •
مع ذلك ،فإن حقيقة أن الحجم جزء من النوع يحد من فائدة هذا اإلصدار من .print •
حيث لن يمكننا استدعاء هذه الدالة إﻻ ملصفوفة من عرشة أعداد صحيحة بالضبط: •
سرنى في الفقرة 16.1.1كيف يمكننا كتابة هذه الدالة بطريقة تسمح لنا بتمرير معلمة مرجع ملصفوفة من أي
حجم.
تذكر أنه ال توجد مصفوفات متعددة األبعاد في لغة يس .++بدال ً من ذلك ،فما تبدو كمصفوفة متعددة األبعاد ما
هي إﻻ مصفوفة من مصفوفات أخرى.
وكما هو حال أي مصفوفة ،يتم تمرير املصفوفة متعددة األبعاد كمؤرش ألول عنرص فيها. •
ونظرًا ألننا نتعامل مع مصفوفة من مصفوفات ،فإن ذلك العنرص سيكون مصفوفة ، •
وبالتالي فإن املؤرش هو مؤرش ملصفوفة. •
يعد حجم البُعد الثاني (وأي بُعد الحق) هو جزء من نوع العنرص ويجب تحديده: •
//matrix points 1st element in array whose elements are arrays of ten ints
} void print(int (*matrix)[10], int rowSize) { /* ... */
ملحوظة
التمرين :6.21
-اكتب دالة تأخذ intومؤرشًا إلى intوتعيد أكرب قيمة intأو القيمة اليت يشري إليها املؤرش.
-ما النوع الذي يجب أن تستخدمه للمؤرش؟
التمرين :6.22
اكتب دالة لتبديل مؤرشين .int
التمرين :6.23
-اكتب نسختك الخاصة من كل دالة من دوال printاملعروضة في هذا القسم.
-قم باستدعاء كل من هذه الدوال لطباعة iو jاملعرفة على النحو التالي:
;}int i = 0, j[2] = {0, 1
تمرين :6.24
-ارشح سلوك الدالة التالية.
-إذا كانت هناك مشاكل في الكود ،فقم برشحها وكيف يمكنك إصالحها.
)]void print(const int ia[10
{
)for (size_t i = 0; i != 10; ++i
;cout << ia[i] << endl
}
يبدو أن الدالة mainتعترب مثاال ً جيداً على كيفية تمرير برامج يس ++مصفوفات إلى الدوال.
نعرف mainبقائمة معلمات فارغة :
فحىت اآلن ،كنا ّ
مع ذلك ،نحتاج أحيا ًنا إلى تمرير وسيطات إلى .main •
شيوعا لوسيطات mainهو السماح للمستخدم بتحديد مجموعة خيارات لتوجيه تشغيل
ً االستخدام األكرث •
الربنامج.
فعلى سبيل املثال ،نفرتض أن برنامجنا mainموجود في ملف قابل للتنفيذ يسمى ، prog •
فقد نقوم بتمرير خيارات إلى الربنامج على النحو التالي : •
يتم تمرير خيارات سطر األوامر هذه إلى mainفي معلمتني (اختياريتني):
املعلمة الثانية ، argv ،عبارة عن مصفوفة مؤرشات لسالسل أحرف على نمط .C •
املعلمة األولى ، argc ،تمرر عدد السالسل في تلك املصفوفة. •
ونظرًا ألن املعلمة الثانية عبارة عن مصفوفة ،فيمكننا بدال ً من ذلك تعريف mainكـ : •
} int main(int argc, char **argv) { ...
بالنظر إلى سطر األوامر السابق ،سيكون ، argc 5وستحتفظ argvبسالسل أحرف نمط اليس التالية:
تحذير
تمرين :6.25
اكتب دالة رئيسية تأخذ وسيطتني .اربط الوسيطات املتوفرة واطبع السلسلة الناتجة.
التمرين :6.26
برنامجا يقبل الخيارات املعروضة في هذا القسم.
ً -اكتب
-اطبع قيم الوسيطات اليت تم تمريرها إلى .main
في بعض األحيان ال نعرف مسب ًقا عدد الوسيطات اليت نحتاج تمريرها لدالة.
فعلى سبيل املثال ،قد نرغب في كتابة روتني لطباعة رسائل الخطأ اليت تم إنشاؤها من برنامجنا. •
نود استخدام دالة واحدة لطباعة رسائل الخطأ هذه من أجل التعامل معها بطريقة موحدة .مع ذلك ، •
قد تمرر استدعاءات مختلفة لدالة طباعة األخطاء وسيطات مختلفة تتوافق مع أنواع مختلفة من رسائل •
الخطأ.
يوفر املعيار الجديد طريقتني أساسيتني لكتابة دالة تأخذ عد ًدا متفاو ًتا من الوسيطات :
فإذا كانت جميع الوسيطات تملك نفس النوع ،فيمكننا تمرير نوع مكتبة باسم قائمة_مهيـئ . •
أما إذا اختلفت أنواع الوسيطات ،فيمكننا كتابة نوع خاص من الدوال ، •
تعرف باسم القالب املتغري ،variadic templateوسنغطيه في الفصل. ١٦ •
يمكننا كتابة دالة تأخذ عد ًد ا غري معروف من الوسيطات من نوع واحد باستخدام معلمة قائمة_املهئي .
قائمة املهئي هي نوع مكتبة يمثل مصفوفة قيم من نوع محدد. •
يتم تعريف هذا النوع في العنوان .initializer_list •
يتم رسد العمليات اليت توفرها القائمة في الجدول .6.1 •
عمليات begin and endعلى كائنات قائمة املهئي تشبه أعضاء متجه begin and endاملقابلة. •
يعطينا العضو () beginمؤرشًا ألول عنرص في القائمة ، •
أما () endفهي مؤرش ملا بعد آخر عنرص. •
تهئي دالتنا begلتشري ﻷول عنرص وتتكرر خالل كل عنرص في قائمة املهئي. •
في بنية الحلقة ،قمنا بإلغاء مرجع begمن أجل الوصول إلى العنرص الحالي وطباعة قيمته. •
عندما نمرر سلسلة قيم إلى معلمة ، initializer_listيجب أن نحرص التسلسل بأقواس متعرجة:
هنا نستدعي نفس الدالة ، error_msg ،ونمرر ثالث قيم في أول استدعاء وقيمتني في اﻻستدعاء الثاني. •
أيضا.
يمكن لدالة تحتوي على معلمة قائمة_مهئي أن تحتوي على معلمات أخرى ً •
فعلى سبيل املثال ،قد يحتوي نظامنا للتصحيح على فئة تسمى ، ErrCode •
أنواعا مختلفة من األخطاء.
ً وهي فئة تمثل •
يمكننا مراجعة برنامجنا ألخذ ErrCodeإضافة إلى قائمة املهئي على النحو التالي :
ونظرًا ألن قائمة املهئي تملك أعضاء ، begin and endفيمكننا استخدام forالنطاق ملعالجة العنارص. •
هذا الربنامج ،مثل نسختنا السابقة ،يتكرر عنرصًا في كل مرة خالل قائمة القيم اليت تم تمريرها إلى •
املعلمة .il
الستدعاء هذا اإلصدار ،نحتاج إلى مراجعة استدعاءاتنا لتمرير وسيطة : ErrCode
)if (expected != actual
;)}error_msg(ErrCode(42), {"functionX", expected, actual
else
;)}"error_msg(ErrCode(0), {"functionX", "okay
ُأوجدت معلمات Ellipsisفي يس ++للسماح للربامج بالتفاعل مع كود Cيستخدم قسم مكتبة Cيسمى
.varargs
وعموماً ،ال ينبغي استخدام معلمة عالمة الحذف ألغراض أخرى. •
سوف تصف وثائق مرتجم يس كيفية استخدام .varargs •
تحذير
يحدد أول نموذج النوع (أو األنواع) لبعض معلمات .foo •
يتم فحص الوسيطات اليت تتوافق مع املعلمات املحددة كاملعتاد. •
ﻻ يتم إجراء تدقيق نوع للوسيطات اليت تتوافق مع معلمة عالمة الحذف. •
في هذا النموذج األول ،تكون الفاصلة اليت تلي إعالنات املعلمات اختيارية. •
التمرين :6.27
-اكتب دالة تأخذ >initializer_list <intوتنتج مجموع العنارص في القائمة.
التمرين :6.28
-في اإلصدار الثاني من error_msgالذي يحتوي على معلمة ، ErrCode
-ما هو نوع العنرص املوجود في حلقة for؟
التمرين :6.29
مرجعا كمتغري للتحكم في الحلقة؟
ً -عند استخدام قائمة املهئي في نطاق ،فهل ستستخدم
-إذا كان األمر كذلك ملاذا؟ إذا لم يكن كذلك ،فلماذا؟
6٫3أنــــــــــواع اإلعــــــادة والجملــــــة ِ
أعــــــد
6.3. Return Types and the return Statement
;return
;return expression
يمكن استخدام اإلعادة بدون قيمة فقط في دالة ذات نوع إعادة .void
الدوال اليت تعيد voidليس مطلوبا أن تحتوي على الجملة ٍ
أعد .return •
ففي دالة ، voidتحدث اإلعادة ضمنيا بعد آخر جملة فيها. •
وعاد ًة تستخدم دوال voidجملة ِ
أعد returnللخروج من الدالة عند نقطة متوسطة. •
يشبه استخدام returnهناك استخدام جملة اكرِس breakللخروج من حلقة. •
فعلى سبيل املثال ،يمكننا كتابة دالة مبادلة لن تعمل إن كانت القيم متطابقة : •
تتحقق هذه الدالة أوال ً ما إذا كانت القيم متساوية ،فإن كان األمر كذلك ،تخرج من الدالة. •
أما إذا كانت القيم غري متساوية ،تقوم الدالة بمبادلتها. •
تحدث اإلعادة ضمنياً بعد آخر جملة تعيني. •
قد ﻻ تستخدم دالة ذات نوع إعادة فارغة الشكل الثاني من الجملة ِ
أعد إﻻ إلعادة نتيجة استدعاء دالة أخرى •
فارغا.
ً تعيد
يعد إعادة أي تعبري آخر من دالة فارغة خطأ وقت الرتجمة. •
تعترب الجملة ِ
أعد من داخل حلقة forخطًأ ألنه يفشل في إعادة قيمة. •
ينبغي أن يكتشف املرتجم هذا الخطأ. •
يحدث الخطأ الثاني بسبب فشل الدالة في توفري الجملة ِ
أعد بعد الحلقة. •
إذا استدعينا هذه الدالة بسلسلة واحدة تمثل مجموعة فرعية من السلسلة األخرى ، •
فسيخرج التنفيذ من سلسلة .for •
يجب أن يكون هناك جملة ِ
أعد للتعامل مع تلك الحالة. •
قد يكتشف املرتجم هذا الخطأ وقد ال يكتشفه. •
معرف.
إذا لم يكتشف الخطأ ،فإن ما يحدث في وقت التشغيل سلوك غري ّ •
تحذير
نوع اإلعادة لهذه الدالة هو سلسلة ،ما يعين أن القيمة املعادة يتم نسخها إلى موقع االستدعاء. •
تقوم هذه الدالة بإعادة نسخة من الكلمة ،أو تقوم بإعادة سلسلة مؤقتة غري مسماة ناتجة عن جمع كلمة •
مع نهاية.
وكما هو حال أي مرجع آخر ،عندما تقوم دالة بإعادة مرجع ، •
يكون هذا املرجع مجرد اسم آخر للكائن الذي تشري إليه. •
مرجعا إلى أقرص_سلسلة من معلميت سلسلة خاصة بها :
ً كمثال ،فكر في دالة تعيد •
عندما تكتمل دالة ،يتم تحرير مخزنها .وبعد إنهاءها ،ستشري املراجع لكائنات محلية إلى ذاكرة لم تعد صالحة :
نصيحة
إحدى الطرق الجيدة للتأكد من أمان إعادة ما يكون بطرح السؤال :إلى أي كائن موجود مسب ًقا سيشري املرجع؟
نظر ًا ألن هذه العوامل تجميعية لليسار ،فإن نتيجة دالة أقرص_سلسلة هي املعامل األيرس لعامل النقطة. •
وسيقوم العامل بجلب عضو sizeللمعامل السلسلة ذاك. •
وهذا العضو يعد املعامل األيرس لعامل اﻻستدعاء الثاني. •
يعتمد ما إذا كان استدعاء دالة قيمة يرسى على نوع إعادة الدالة.
فاستدعاءات دوال تعيد مراجع تعترب قيماً يرسى ؛ بينما أنواع اإلعادة األخرى تسفر عن قيم يمىن. •
يمكن استخدام استدعاء دالة تقوم بإعادة مرجع بنفس طرق أي قيمة يرسى أخرى. •
مرجعا لغري ثابت :
ً باﻷخص ،يمكننا تعيني نتيجة دالة تعيد •
قد يكون من املفاجئ رؤية استدعاء دالة على الجانب األيرس من التعيني. •
مع ذلك ،ال يوجد يشء خاص في املوضوع. •
القيمة املعادة هي مرجع ،لذا فإن االستدعاء قيمة يرسى. •
ومثل أي قيمة يرسى أخرى ،قد يظهر كمعامل أيرس لعامل التعيني. •
إذا كان نوع اإلعادة مرجعاً لثابت ،فعندئذ (كاملعتاد) قد ال نعني لنتيجة اﻻستدعاء :
في ظل املعيار الجديد ،يمكن للدوال إعادة قائمة ذات أقواس من قيم.
وكما هو حال أي إعادة أخرى ،يتم استخدام القائمة لتهيئة مؤقت يمثل معاد الدالة. •
إذا كانت القائمة خالية ،يتم تهيئة القيمة املؤقتة. •
عدا ذلك ،فإن قيمة اإلعادة تعتمد على نوع إعادة الدالة. •
على سبيل املثال ،تذكر دالة error_msgفقرة . 6.2.6
أخذت تلك الدالة عد ًدا متفاو ًتا من وسيطات السلسلة وطبعت رسالة خطأ مكونة من سالسل معينة. •
بدال ً من استدعاء ، error_msgسنقوم في هذه الدالة بإعادة متجه يحمل سالسل رسائل الخطأ : •
)(vector<string> process
{
// ...
// expected and actual are strings
))(if (expected.empty
;}{ return // return empty vector
)else if (expected == actual
return {"functionX", "okay"}; //return list-initialized vector
else
;}return {"functionX", expected, actual
}
في الجملة ِ
أعد األولى ،نعيد قائمة خالية. •
في هذه الحالة ،سيكون املتجه الذي تعيد إليه processخالياً .عدا ذلك ، •
متجها تمت تهيئته على عنرصين أو ثالثة اعتما ًدا على كون expectedو actualمتساويني.
ً سنعيد •
نوعا مدمجاً ،ربما تحتوي قائمة اﻷقواس على قيمة واحدة على األكرث ،
في دالة تعيد ً •
مضي ًقا.
ّ ويجب أال تتطلب هذه القيمة تحوياًل •
فستعرف الفئة نفسها كيفية استخدام املهئي.
ّ وإن قامت دالة بإعادة نوع فئة ، •
هناك استثناء لقاعدة تنص على أنه يجب أن تعيد الدالة ذات نوع اإلعادة غري الفارغة (أن تعيد) قيمة ما :
حيث يُسمح لدالة mainباإلنهاء دون إعادة قيمة. •
ضمنيا بإدراج اإلعادة صفر.
ً إذا وصلت أداة التحكم إلى نهاية mainدون إعادة ،سيقوم املرتجم •
وكما رأينا في الفقرة ، 1.1يتم التعامل مع القيمة املعادة من mainكمؤرش حالة. •
فالصفر يشري إلى النجاح ؛ بينما تشري معظم القيم األخرى إلى الفشل. •
وتلك القيمة الغري صفرية يعتمد معناها على اآللة. •
ولجعل القيم املعادة مستقلة عن اآللة ، •
يحدد العنوان cstdlibمتغريي معالج أولي نستخدمهما لإلشارة إلى النجاح أو الفشل : •
)(int main
{
)if (some_failure
;return EXIT_FAILURE // defined in cstdlib
else
;return EXIT_SUCCESS // defined in cstdlib
}
ونظرًا ألن هذه متغريات معالج أولي ،فيجب أال يسبقها ، std::وأال نذكرها في إعالنات .using
الدالة اليت تستدعي نفسها ،سواء بشكل مبارش أو غري مبارش ،هي دالة استدعاء ذاتي .recursive
مثالً ،يمكننا إعادة كتابة دالة املرضوب باستخدام استدعاء ذاتي :
ملحوظة
التمرين :6.30
-قم بتجميع إصدار str_subrangeآخر كاملعروض في فقرة ٦,٣,٢ملعرفة ما يحدث لألخطاء املشار إليها.
التمرين :6.31
-ما هي صالحية إعادة مرجع؟ مرجع لثابت؟
التمرين :6.32
-حدد ما إذا كانت الدالة التالية رشعية أم ال .فإذا كانت كذلك ،ارشح ما تفعله ؛
-إذا لم تكن كذلك ،فقم بتصحيح أي أخطاء ثم ارشحها.
)int &get(int *arry, int index
} ;]{ return arry[index
{ )(int main
;]int ia[10
)for (int i = 0; i != 10; ++i
;get(ia, i) = i
}
التمرين :6.33
-اكتب دالة استدعاء ذاتي لطباعة محتويات متجه.
التمرين :6.34
ماذا سيحدث إذا كان التالي هو مسار التوقف في العامل
)if (val != 0
التمرين :6.35
-في اﻻستدعاء لـ ، factملاذا مررنا val - 1بدال ً من - val؟
ألنه ال يمكننا نسخ مصفوفة ،فال يمكن لدالة إعادة مصفوفة .مع ذلك ،
مرجعا ملصفوفة ،لكن يعد بناء جملة لتعريف تلك الدالة عملية مخيفة.
ً يمكن لدالة أن تعيد مؤرشًا أو •
لحسن الحظ ،لدينا طرق لتبسيط مثل تلك اإلعالنات. •
الطريقة األكرث مبارشة هي استخدام اسم نوع مستعار : •
;]typedef int arrT[10 // arrT is synonym for type array of ten ints
;]using arrtT = int[10 ;// equivalent declaration of arrT
;)arrT* func(int i // func returns pointer to array of five ints
لإلعالن عن funcدون استخدام اسم نوع مستعار ،يجب أن نتذكر أن حجم مصفوفة يتبع االسم الذي يتم
تعريفه :
كما هو حال هذه اإلعالنات ،إن أردنا تعريف دالة تقوم بإعادة مؤرش ملصفوفة ،يجب أن يتبع الحجم اسم •
الدالة.
أيضا .قائمة املعلمات تسبق الحجم.
مع ذلك ،تتضمن الدالة قائمة معلمات تتبع االسم ً •
ومن ثم ،فإن نموذج دالة تعيد مؤرشًا ملصفوفة هو : •
]Type (*function(parameter_list))[dimension
وكما هو حال أي إعالن مصفوفة آخر Type ،هو نوع العنارص و dimensionهو حجم املصفوفة. •
أما األقواس حول () )*function(parameter_listرضورية لنفس السبب الذي كانت مطلوبة فيه •
عندما عرفنا .p2
فبدون تلك اﻷقواس ،سنقوم بتعريف دالة تقوم بإعادة مصفوفة من املؤرشات. •
كمثال ملموس ،يعلن الشكل التالي funcدون استخدام اسم نوع مستعار :
لفهم هذا اإلعالن ،قد يكون من املفيد التفكري فيه على النحو التالي:
تخرب أنه يمكننا استدعاء funcباستخدام وسيطة .int )func(int i
تخرب أنه يمكننا إزالة مرجع نتيجة ذلك اﻻستدعاء. ))(*func(int i
تخرب أن إزالة مرجع نتيجة استدعاء funcينتج مصفوفة بحجم عرشة. ](*func(int i))[10
تخرب أن نوع العنرص في تلك املصفوفة هو .int ]int(*func(int i))[10
// func takes int argument and returns pointer to array of ten ints
;]auto func(int i) -> int(*)[10
ونظرًا ألن نوع اإلعادة يأتي بعد قائمة معلمات ، •
فمن األسهل رؤية أن funcتعيد مؤرشًا وأن هذا املؤرش يشري ملصفوفة من عرشة أعداد صحيحة .ints •
بديل آخر ،إذا عرفنا أن املصفوفة (ــات) اليت يمكن لدالتنا إعادة مؤرش لها ،فيمكننا استخدام النوع-املعلن
decltypeلإلعالن عن نوع اإلعادة.
فعلى سبيل املثال ،تعيد الدالة التالية مؤرشًا إلحدى مصفوفتني ،اعتما ًدا على قيمة معلمتها :
يستخدم نوع اإلعادة لـ arrPtrالنوع-املعلن ليخرب أن الدالة تعيد مؤرشًا لنوع أيا كان يحتوي على .odd •
هذا الكائن عبارة عن مصفوفة ،لذا تُعيد arrPtrمؤرشًا ملصفوفة من خمسة .ints •
تلقائيا بتحويل املصفوفة إلى نوع
ً الجزء الصعب الوحيد هو أننا يجب أن نتذكر أن النوع-املعلن ال يقوم •
املؤرش املقابل لها.
النوع الذي تم إعادته بواسطة النوع-املعلن هو نوع مصفوفة ، •
ويجب أن نضيف إليه * ليشري إلى أن arrPtrتُعيد مؤرشًا. •
التمرين :6.36
-اكتب اإلعالن عن دالة تقوم بإعادة مرجع ملصفوفة من عرش سالسل ،دون استخدام إعادة متأخر أو نوع-معلن أو
اسم نوع مستعار.
التمرين :6.37
-اكتب ثالثة إعالنات إضافية للدالة في التمرين السابق .حيث يجب عليك استخدام اسم نوع مستعار في إحداها ،
-واستخدام إعادة متأخر في الثانية ،
-وفي الثالثة يجب أن يستخدم النوع-املعلن.
-ما الشكل الذي تفضله وملاذا؟
التمرين :6.38
قم بمراجعة الدالة arrPtrعند تشغيلها لتعيد مرجعا ملصفوفة.
6٫4الــــــدوال زائــــــــــدة الـتعبئــــــة
6.4. Overloaded Functions
الدوال اليت لها نفس االسم ولكن بقوائم معلمات مختلفة واليت تظهر في نفس النطاق تعرف بالدوال زائدة التعبئة .
على سبيل املثال ،في الفقرة 6.2.4قمنا بتعريف عدة دوال تسمى : print
تؤدي هذه الدوال نفس اإلجراء العام لكنها تنطبق على أنواع مختلفة من املعلمات. •
بناء على نوع الوسيط الذي نمرره :
ً عندما نستدعي هذه الدوال ،يمكن للمرتجم استنتاج الدالة اليت نريدها •
تزيل زيادة التعبئة الحاجة إلى ابتكار -وتذكر -األسماء املوجودة فقط ملساعدة املرتجم على معرفة الدالة اليت يجب
استدعاءها.
ملحوظة
في الزوج األول ،يسمي أول إعالن معامله .أسماء املعلمات ليست إﻻ مساعدة في التوثيق. •
ال تتغري قائمة املعلمات. •
نوعا جدي ًدا ؛ هو مرادف لـ .Phone
أما في الزوج الثاني ،فيبدو أن األنواع مختلفة ،لكن TelNoليس ً •
نوعا جدي ًدا.
اسما بديالً لنوع موجود ؛ لكن ال يُنئش ً
ً يوفر اسم النوع املستعار •
مستعارا ،
ً اسما
ً لذلك ،هناك معلمتان تختلفان فقط في أن أحدهما يستخدم •
واآلخر يستخدم نوعاً يتوافق معه االسم املستعار وال يختلفان. •
كما رأينا في الفقرة ،3.2.6ال يؤثر ثابت املستوى العالي على كائنات يمكن تمريرها إلى الدالة.
ال يمكن تميزي معلمة تحتوي على قيمة ثابتة عالية املستوى عن معلمة ال تحتوي عليها :
;)Record lookup(Phone
;)Record lookup(const Phone )// redeclares Record lookup(Phone
;)*Record lookup(Phone
;)Record lookup(Phone* const )*// redeclares Record lookup(Phone
في هذه اإلعالنات ،يعلن اإلعالن الثاني عن نفس دالة اإلعالن األول .لكن من ناحية أخرى ، •
مرجعا (أو مؤرشًا) لثابت أو غري ثابت لنوع معني ؛
ً يمكننا زيادة التعبئة استنا ًدا على ما إذا كانت املعلمة •
مثل هذه الثوابت تعد منخفضة املستوى : •
// functions taking const & nonconst references or pointers have different
parameters
// declarations for four independent, overloaded functions
;)&Record lookup(Account // function takes reference to Account
;)&Record lookup(const Account // new function takes const reference
;)*Record lookup(Account // new function takes pointer to Account
;)*Record lookup(const Account // new function takes pointer to const
في هذه الحاالت ،يمكن للمجمع استخدام ثبات الوسيطة لتميزي الدالة اليت يجب استدعاؤها. •
ونظر ًا لعدم وجود تحويل من الثابت ،يمكننا تمرير كائن ثابت (أو مؤرش لثابت) فقط إلى إصدار يحتوي •
على معلمة ثابتة.
ونظر ًا لوجود تحويل إلى ثابت ،يمكننا استدعاء دالة على كائن غري ثابت أو مؤرش لغري ثابت. •
مع ذلك ،كما سرنى في الفقرة ، 6.6.1 •
سيفضل املرتجم اإلصدار غري ثابت عندما نقوم بتمرير كائن أو مؤرش غري ثابت لغري ثابت. •
عندما ال يصح زيادة تعبئة اسم دالة تلميح :
When Not to Overload a Function Name
بالرغم من أن زيادة التعبئة تتيح لنا تجنب الحاجة إلى اخرتاع (أو تذكر) أسماء عمليات شائعة ، •
إال أنه ينبغي علينا فقط زيادة التعبئة على عمليات تقوم بالفعل بأشياء مماثلة. •
هناك بعض الحاالت اليت يكون فيها توفري أسماء دوال مختلفة إضافة تجعل معلومات الربنامج أسهل •
في الفهم.
مثالً ،فكر في مجموعة دوال تحرك مؤرشاً على الشاشة. •
;)(Screen& moveHome
;)Screen& moveAbs(int, int
;)Screen& moveRel(int, int, string direction
قد يبدو ألول وهلة أنه من األفضل زيادة تعبئة مجموعة الدوال هذه تحت اسم : move
;)(Screen& move
;)Screen& move(int, int
;)Screen& move(int, int, string direction
لكن ،من خالل زيادة التعبئة على هذه الدوال ،سنفقد معلومات متأصلة في أسماءها. •
بالرغم من كون حركة املؤرش عملية عامة تشرتك فيها كل تلك الدوال ، •
إال أن الطبيعة املحددة لتلك الحركة فريدة لكل دالة منها. •
يمثل - moveHomeمثالً -حركة مؤرش خاصة. •
تعتمد زيادة تعبئة هاتني الدالتني على سهولة فهم هذين اﻻستدعاءين : •
?// which is easier to understand
!myScreen.moveHome(); // we think this one
;)(myScreen.move
الحظنا في الفقرة 3.11.4أن القولبة الساكنة مفيدة للغاية في سياق الدوال زائدة التعبئة.
كمثال ،تذكر دالة أقرص_سلسلة من الفقرة : 2.3.6
يستدعي هذا اإلصدار اإلصدار الثابت من أقرص_سلسلة عن طريق قولبة وسيطاته إلى مراجع لثوابت. •
تعيد هذه الدالة مرجعاً لسلسلة ثابتة ،واليت نعرف أنها مرتبطة بواحدة من وسيطاتنا األصلية غري •
الثابتة.
لذلك ،نعلم أنه من اآلمن قولبة هذه السلسلة إلى سلسلة عادية في الجملة ِ
أعد. •
نعر ف مجموعة دوال زائدة التعبئة ،نحتاج إلى أن نكون قادرين على استدعائها بالوسيطات املناسبة.
بمجرد أن ّ
أيضا باسم دقة زيادة التعبئة) هي عملية ...
مطابقة الدالة (املعروفة ً •
يتم من خاللها ربط استدعاء دالة معينة بدالة محددة من مجموعة دوال زائدة التعبئة. •
يحدد املرتجم الدالة اليت يجب استدعاؤها ، •
وذلك من خالل مقارنة الوسيطات في االستدعاء باملعلمات اليت توفرها كل دالة في مجموعة زيادة التعبئة. •
لكن في الوقت الحالي ،من املهم أن ندرك أنه بالنسبة ألي استدعاء لدالة زائدة التعبئة ،هناك ثالث نتائج محتملة:
يجد املرتجم دالة واحدة بالضبط تعد أفضل تطابق للوسيطات الفعلية ويقوم بتوليد كود الستدعاءها. •
ال توجد دالة ذات معلمات تطابق الوسيطات في االستدعاء ، •
وهنا يُصدر املرتجم رسالة خطأ تفيد بعدم وجود تطابق. •
أن يكون هناك أكرث من دالة تتطابق مع عدم وجود أي تطابقات أفضل. •
استدعاء غامضاً.
ً هنا أيضا خطأ؛ إنه يعد •
التمرين :6.39
-ارشح تأثري اإلعالن الثاني في كل مجموعة من مجموعات اإلعالنات التالية.
-حدد أيها غري رشعي ،إن وجد.
)(a ;)int calc(int, int
;)int calc(const int, const int
تحذير
في العادة ،يعترب القيام بإعالن دالة محلياً فكرة سيئة وغري موفقة .مع ذلك ،
ومن أجل رشح كيفية تفاعل النطاق مع زيادة التعبئة ،سننتهك هذه املمارسة ونقوم بإعالنات دوال محلياً.
غالب ا ما يكون خلط لدى املربمجني الجدد في يس ++حول التفاعل بني النطاق وزيادة التعبئة.
ً
ومع ذلك ،فإن زيادة التعبئة ليست له خصائص خاصة فيما يتعلق بالنطاق: •
كالعادة ،إذا أعلنا عن اسم في نطاق داخلي ، •
فإن هذا االسم يخفي استخدامات ذلك االسم املعلن في نطاق خارجي. •
األسماء ﻻ تكون زائدة التعبئة عرب النطاقات: •
;)(string read
;)& void print(const string
;)void print(double // overloads the print function
)void fooBar(int ival
{
bool read = false; // new scope: hides outer declaration of read
string s = read(); // error: read is bool variable, not function
// bad practice : usually bad idea to declare functions at local scope
;)void print(int // new scope : hides previous instances of print
;)" print("Value: // error: print(const string &) is hidden
;)print(ival // ok: print(int) is visible
;)print(3.14 // ok: calls print(int); print(double) is hidden
}
ملحوظة
سنغطي في هذا القسم ثالث مزيات مرتبطة بالدوال ستكون مفيدة في العديد من الربامج ،ولكن ليس كلها:
الوسيطات االفرتاضية ،ودوال ضمن-السطر ودوال التعبري الثابت ، •
إضافة لبعض اﻷقسام اليت تُستخدم غالبًا أثناء تصحيح األخطاء. •
تحتوي بعض الدوال على معلمات تُمنح قيمة معينة في معظم اﻻستدعاءات وليس كلها.
في مثل هذه الحاالت ،يمكننا إعالن تلك القيمة املشرتكة كوسيطة افرتاضية لدالة. •
يمكن استدعاء الدوال ذات الوسيطات االفرتاضية بتلك الوسيطة أو بدونها. •
على سبيل املثال ،قد نستخدم سلسلة لتمثيل محتويات نافذة. •
بشكل افرتايض ،قد نرغب في أن يكون للنافذة ارتفاع وعرض وخلفية معينة. •
أيضا في السماح للمستخدمني بتمرير قيم غري تلك القيم االفرتاضية.
مع ذلك ،قد نرغب ً •
الستيعاب كل من القيم االفرتاضية واملحددة ،نعلن عن دالتنا لتعريف النافذة على النحو التالي: •
إذا أردنا استخدام وسيطة افرتاضية ،فإننا نحذف تلك الوسيطة عندما نستدعي الدالة.
ونظرًا ألن screenتوفر اإلعدادات االفرتاضية لجميع معلماتها ، •
فيمكننا استدعاء screenبال وسيطة أو وسيطة واحدة أو اثنني أو ثالث وسيطات : •
;string window
;)(window = screen )' '// equivalent to screen(24,80,
;)window = screen(66 )' '// equivalent to screen(66,80,
;)window = screen(66, 256 )' '// screen(66,256,
;)'window = screen(66, 256, '# )'// screen(66,256,'#
;)'?' window = screen(, , // error: can omit only trailing arguments
;)'?'(window = screen )' '// calls screen('?',80,
الحظ أن االستدعاء الثاني ،الذي يمرر قيمة حرف واحد ،يعد رشعياً. •
لكن بالرغم من رشعيته ،إﻻ أنه من غري املرجح أن يكون هو املقصود. •
اعترب اﻻستدعاء رشعياً بسبب أن "؟" يعد حرفا ،ويمكن تحويل الحرف إلى نوع املعلمة املوجودة في أقىص •
اليسار.
هذه املعلمة عبارة عن ، string::size_typeوهي نوع عدد صحيح بال_إشارة. •
في هذا االستدعاء ،يتم تحويل وسيطة charضمناً إلى ، string::size_typeوتمريرها كوسيطة •
لالرتفاع.
على أجهزتنا ،قيمة "؟" كسدايس عرشي ستساوي ، 0x3Fوهي مكافئة للرقم العرشي .63 •
وهكذا ،فإن هذا االستدعاء سيمرر الرقم 63إلى معلمة االرتفاع. •
يتمثل جزء من عمل تصميم دالة باستخدام وسيطات افرتاضية في ترتيب املعلمات
بحيث تظهر تلك األقل احتمالية الستخدامها أوال ً ، •
بينما تظهر تلك اليت يرجح أن تستخدم القيمة االفرتاضية أخريًا. •
بالرغم من أنه من املعتاد إعالن دالة مرة واحدة داخل عنوان ،فمن الرشعي إعادة تعريف الدالة عدة مرات.
ومع ذلك ،يمكن تحديد اإلعداد االفرتايض لكل معلمة مرة واحدة فقط في نطاق معني .بالتالي ، •
ﻻ يمكن ألي إعالن الحق إضافة قيمة افرتاضية إﻻ ملعامل لم يتم تحديد اﻻفرتايض له مسب ًقا من قبل. •
وكالعادة ،ال يمكن تحديد القيم االفرتاضية إال إذا كانت جميع املعلمات على اليمني بها قيم افرتاضية •
بالفعل.
مثالً -بافرتاض : •
أفضل املمارسات
// the declarations of wd, def, and ht must appear outside function
;sz wd = 80
;' ' = char def
;)(sz ht
;)string screen(sz = ht(), sz = wd, char = def
)' ' string window = screen(); // calls screen(ht(), 80,
)(void f2
{
;'*' = def // changes value of default argument
sz wd = 100;// hides outer definition of wd but does not change default
;)(window = screen )'*' // calls screen(ht(), 80,
}
التمرين :6.41
-أي من اﻻستدعاءات التالية غري رشعية ،إن وجدت؟ ملاذا ا؟
-أيها رشعي ،إن وجد ،ولكن من غري املحتمل أن يتطابق مع نية املربمج؟ ملاذا ا؟
;)' ' = char *init(int ht, int wd = 80, char bckgrnd
;)((a) init
;)(b) init(24,10
;)'*' (c) init(14,
التمرين :6.42
-أعط املعلمة الثانية لـ make_pluralالوسيطة االفرتاضية لـ "."s
-اخترب برنامجك عن طريق طباعة صيغ املفرد والجمع لكلمات .success and failure
6.5.2. Inline and constexpr Functions 6٫5٫2دوال ضمن-السطر ودوال تعبري ثابت
في الفقرة 2.3.6كتبنا دالة صغرية أعادت مرجعاً ألقرص معلمات سلسلة .فوائد تعريف دالة ملثل هذه العملية
الصغرية تشمل :
• يعد من األسهل قراءة وفهم استدعاء أقرص_سلسلة من قراءة وفهم التعبري الرشطي املكافئ.
استخدام دالة يضمن سلو ًكا موح ًدا .يضمن إجراء كل اختبار أن يتم بنفس الطريقة. •
إذا احتجنا إلى تغيري الحساب ،فمن األسهل تغيري الدالة بدال ً من البحث عن كل تكرار للتعبري املكافئ •
وتغيريه.
يمكن إعادة استخدام الدالة بدال ً من إعادة كتابتها لتطبيقات أخرى. •
لكن مع ذلك ،هناك عيب واحد محتمل من إنشاء الدالة أقرص_سلسلة :
يحتمل أن يكون استدعاء دالة أبطأ من تقييم تعبري مكافئ. •
ففي معظم األجهزة ،يقوم استدعاء الدالة بالكثري من العمل: •
مثل حفظ السجالت قبل اﻻستدعاء واستعادتها بعد اإلعادة ؛ •
واحتمال نسخ الوسيطات ؛ ونقل فروع الربنامج إلى موقع جديد. •
يتم توسيع تعريف الدالة املحددة على أنها ضمن-السطر "( "inlineعاد ًة) في كل استدعاء.
إذا تم تعريف أقرص_سلسلة على أنها ضمن-السطر ،فهذا اﻻستدعاء :
(على األرجح) سيتم توسيعه أثناء التجميع إلى يشء مثل :
ملحوظة
تخصيص ضمن-السطر ما هو إﻻ طلب للمرتجم .فقد يختار املرتجم تجاهل ذلك الطلب.
بشكل عام ،تهدف آلية ضمن-السطر إلى تحسني دوال خط صغرية يتم استدعاؤها بشكل متكرر. •
لن يتم تضمني دالة استدعاء ذاتي من قبل العديد من املرتجمات. •
يكاد يكون من املؤكد أنه لن يتم توسيع دالة تتكون من 75سطرًا على أنها ضمن-السطر. •
ستعيد دالة املقياس scaleتعبريًا ثاب ًتا إذا كانت الوسيطة الخاصة بها تعبريًا ثاب ًتا ولكن ليس بخالف ذلك:
عندما نمرر تعبريًا ثاب ًتا -مثل القيمة الحرفية - 2فإن اﻹعادة تكون تعبريًا ثاب ًتا. •
في هذه الحالة ،سوف يستبدل املرتجم استدعاء scaleبالقيمة الناتجة. •
أما إذا استدعينا scaleبتعبري ليس ثاب ًتا -مثل الكائن - int iفإن اﻹعادة لن تكون تعبريًا ثاب ًتا. •
إذا استخدمنا scaleفي سياق يتطلب تعبريًا ثاب ًتا ،يتحقق املرتجم أن النتيجة هي تعبري ثابت. •
فإن لم تكن كذلك ،سيصدر املرتجم رسالة خطأ. •
ملحوظة
على عكس الدوال األخرى ،يمكن تعريف الدوال ضمن-السطر و التعبري الثابت عدة مرات في الربنامج.
بكل اﻷحوال ،يحتاج املرتجم إلى التعريف -وليس اإلعالن فقط -من أجل توسيع الكود. •
تماما.
ً ومع ذلك ،يجب أن تتطابق جميع تعريفات ضمن-السطر أو التعبري الثابت املحددة •
نتيجة لذلك ،يتم تعريف دوال ضمن-السطر و التعبري الثابت عاد ًة في العناوين. •
التمرين :6.44
-أعد كتابة دالة isShorterمن الفقرة 2.2.6لتكون ضمن-السطر.
التمرين :6.45
-راجع الربامج اليت كتبتها للتدريبات السابقة وحدد ما إذا كان ينبغي تحديدها على أنها ضمن-السطر.
-إذا كان األمر كذلك ،فافعل ذلك .إذا لم يكن األمر كذلك ،ارشح ملاذا ال ينبغي أن يكونوا ضمن-السطر.
التمرين :6.46
-هل من املمكن تعريف isShorterباعتباره تعبري ثابت؟
-إذا كان األمر كذلك ،فافعل ذلك .إذا لم يكن كذلك ،ارشح ملاذا ال.
يقيم التعبري exprفإن كان خاطًئ ا (أي صفر) ،فيكتب التوكيد رسالة وينهي الربنامج. •
صفريا) ،ال يفعل التوكيد شيًئ ا.
ً صحيحا (أي ليس
ً وإن كان •
وكما هو حال متغريات املعالج اﻷولي ،يجب أن تكون أسماء املاكرو فريدة داخل الربنامج.
فال يسمح للربامج اليت تتضمن العنوان cassertتعريف متغري أو دالة أو كائن آخر يسمى .assert •
عملياً ،من الجيد تجنب استخدام االسم assertألغراضنا الخاصة حىت لو لم نقم بتضمني .cassert •
حيث تتضمن العديد من العناوين العنوان ، cassert •
ما يعين أنه حىت إن لم تقم بتضمني هذا امللف بشكل مبارش ،فمن املحتمل أنه قد تم تضمينه في برامجك •
على أية حال.
غالب ا ما يتم استخدام ماكرو التوكيد لفحص رشوط "ال يمكن أن تحدث" .فعلى سبيل املثال ،
ً
دائما ما تكون أطول
ً • قد يعلم برنامج يقوم بمعالجة بعض نصوص مدخالت أن جميع الكلمات اليت أدخلت
من الحد.
• قد يحتوي برنامج كهذا على جملة مثل :
يعتمد سلوك التوكيد على حالة متغري معالج أولي يسمى .NDEBUG
إذا تم تعريف ، NDEBUGفال يفعل التوكيد شيًئ ا. •
افرتاضياً ،لم يتم تعريف ، NDEBUGولذلك يقوم التوكيد افرتاضياً بإجراء فحص وقت التشغيل. •
يمكننا "إطفاء" تصحيح األخطاء من خالل توفري #defineلتعريف .NDEBUG •
بدال ً من ذلك ،توفر معظم املرتجمات خيارا في سطر األوامر يتيح لنا تعريف متغريات معالج أولي : •
هذا السطر له نفس تأثري كتابة #define NDEBUGفي بداية .main.C •
إذا تم تعريف ، NDEBUGفإننا نتجنب أعباء محتملة وقت التشغيل تتضمن فحص أوضاع مختلفة. •
أيضا فحص وقت تشغيل.
بالطبع ،ال يوجد ً •
لذلك ،يجب استخدام التوكيد فقط للتحقق من أشياء ال ينبغي أن تكون ممكنة ح ًقا. •
ويمكن أن يكون مفي ًدا كعامل مساعد في تصحيح أخطاء الربنامج ، •
ولكن ال ينبغي استخدامه كبديل لعمليات التحقق من منطق وقت التشغيل ، •
أو التحقق من األخطاء اليت يجب أن يقوم بها الربنامج. •
إضافة إلى استخدام التوكيد ،يمكننا كتابة كود تصحيح رشطي خاص بنا باستخدام .NDEBUG
إذا لم يتم تعريف ، NDEBUGيتم تنفيذ الكود بني #ifndefو .#endif •
إذا تم تعريف ، NDEBUGسيتم تجاهل الكود : •
هنا نستخدم متغري اسمه _ _ _ _funcلطباعة اسم الدالة اليت نقوم بتصحيحها. •
يعرف املرتجم _ _ _ _funcفي كل دالة.
ّ •
وهي عبارة عن مصفوفة ثابتة محلية من الحرف الثابت الذي يحمل اسم الدالة. •
وإضافة إلى _ _ ، _ _funcاليت يعرفها مرتجم يس، ++ •
يعرف املعالج اﻷولي أربعة أسماء أخرى يمكن أن تكون مفيدة في تصحيح األخطاء: •
إذا أعطينا هذا الربنامج سلسلة أقرص من الحد األدنى ،فسيتم إنشاء رسالة الخطأ التالية:
التمرين :6.48
استخداما جي ًدا للتوكيد:
ً ارشح ما تفعله هذه الحلقة وما إذا كانت
assert:
;string s
while (cin >> s && s != sought) { } // empty body
;)assert(cin
6٫6مطــــــــــابقة دالــــــة
6.6. Function Matching
في العديد من الحاالت (إن لم يكن معظمها) ،من السهل معرفة دالة زائدة التعبئة تتطابق مع استدعاء معني.
مع ذلك ،فإن األمور ليست بتلك البساطة عندما يكون للدوال زائدة التعبئة نفس عدد املعلمات ، •
وعندما يكون لواحد أو أكرث من املعلمات أنواع مرتبطة بالتحويالت. •
فكر في مجموعة الدوال واالستدعاء التالي :
وكمثال ّ ،
;)(void f
;)void f(int
;)void f(int, int
;)void f(double, double = 3.14
;)f(5.6 )// calls void f(double, double
تحدد أول خطوة ملطابقة الدالة مجموعة الدوال زائدة التعبئة اليت يتم النظر فيها لالستدعاء.
الدوال في هذه املجموعة هي الدوال املرشحة. •
الدالة املرشحة هي دالة تحمل نفس اسم الدالة اليت تم استدعاؤها •
مرئيا عند نقطة االستدعاء.
ً واليت يكون اإلعالن عنها •
في هذا املثال ،توجد أربع دوال مرشحة تسمى .f •
قد تكون الدالة اليت تأخذ عدداً صحيحاً واحداً والدالة اليت تأخذ مزدوجني doubleقابلني للتطبيق.
يمكن استدعاء أي من هاتني الدالتني باستخدام وسيطة واحدة. •
الدالة اليت تأخذ مزدوجي doubleتملك وسيطة افرتاضية ، •
ما يعين أنه يمكن استدعاؤها باستخدام وسيطة واحدة. •
ملحوظة
عندما تحتوي دالة على وسيطات افرتاضية ،فقد تبدو وسيطات االستدعاء أقل مما هي عليه بالفعل.
بعد استخدام عدد الوسيطات الكتشاف الدوال املرشحة ،سننظر بعد ذلك في ما إذا كانت أنواع الوسيطات تتطابق
مع تلك الخاصة باملعلمات.
• وكما هو حال أي استدعاء ،قد تتطابق الوسيطة مع معلمتها
تماما أو بسبب وجود تحويل من نوع الوسيطة إلى نوع املعلمة.
ً إما ألن األنواع تتطابق •
في هذا املثال ،كلتا الدالتني املتبقيتني قابلتان للتطبيق: •
) f(intـ قابلة للتطبيق ألنه يوجد تحويل يقبل تحويل وسيطة النوع املزدوج إلى معلمة نوع .int •
) f(double ،doubleـ قابلة للتطبيق أيضاً ألنه تم توفري وسيطة افرتاضية للمعلمة الثانية ، •
تماما نوع املعلمة.
ً أما معلمتها األولى من نوع املزدوج ،وهي تطابق •
ملحوظة
إذا لم تكن هناك دوال قابلة للتطبيق ،فسيشتكي املرتجم من عدم وجود دالة مطابقة.
Finding the Best Match, If Any العثور على التطابق اﻷفضل – إن وجد –
تحدد الخطوة الثالثة ملطابقة الدالة أي دالة قابلة للتطبيق توفر أفضل تطابق لالستدعاء.
تبحث هذه العملية في كل وسيطة في اﻻستدعاء ، •
وتختار الدالة (أو الدوال) القابلة للتطبيق اليت تتطابق املعلمة املقابلة معها بشكل أفضل. •
سنرشح تفاصيل "األفضل" في القسم التالي ، •
ولكن الفكرة هي أنه كلما اقرتبت أنواع الوسيطة واملعلمة من بعضها ،كان التطابق أفضل. •
في حالتنا ،هناك وسيطة واحدة (رصيحة) في اﻻستدعاء .هذه الوسيطة نوعها مزدوج. •
فمن أجل استدعاء) ، f(intيجب تحويل الوسيطة من doubleإلى .int •
أما الدالة األخرى القابلة للتطبيق ) ، f(double،doubleهي مطابقة تماماً لهذه الوسيطة. •
تعد املطابقة التامة أفضل من املطابقة اليت تتطلب تحويالً. •
لذلك ،سيقوم املرتجم بحل االستدعاء) f(5.6كاستدعاء للدالة اليت تحتوي على معلميت مزدوج .double
ويضيف الوسيطة االفرتاضية للوسيطة الثانية املفقودة.
Function Matching with Multiple Parameters مطابقة دالة ذات معلمات متعددة
تكون مطابقة الدوال أكرث تعقي ًد ا إن كان هناك وسيطتان أو أكرث .وبالنظر إلى نفس الدوال املسماة ، fفلنحلل
االستدعاء التالي:
;)f(42, 2.56
يتم تحديد مجموعة الدوال القابلة للتطبيق بنفس الطريقة كما هو الحال عند وجود معلمة واحدة فقط. •
يقوم املرتجم بتحديد الدوال اليت تحتوي على العدد املطلوب من املعلمات واليت تتوافق معها أنواع •
الوسيطات مع أنواع املعلمات.
في هذه الحالة ،الدوال القابلة للتطبيق هي ) f(int، intو).f(double، double •
ثم يحدد املرتجم وسيطة بوسيطة ،عن أي دالة (أو دوال) أفضل تطاب ًقا. •
هناك أفضل تطابق عام إذا كانت هناك دالة واحدة فقط فيها •
3التطابق لكل وسيطة ليس أسوأ من التطابق الذي تتطلبه أي دالة أخرى قابلة للتطبيق ✔
توجد وسيطة واحدة على األقل تكون فيها املطابقة أفضل من املطابقة اليت توفرها أي دالة أخرى قابلة ✔
للتطبيق
إذا لم تكن هناك دالة واحدة مفضلة بعد النظر في كل وسيطة ،فإن االستدعاء يكون خاطًئ ا .وسيشتكي املرتجم من
أن اﻻستدعاء غامض.
تماما.
ً • في هذا االستدعاء ،عندما ننظر فقط إلى أول وسيطة سنجد أن الدالة) f(int، intمطابقة
ملطابقة الدالة الثانية ،يجب تحويل الوسيطة int 42إلى مزدوج. •
املطابقة من خالل التحويل املدمج هي "أقل جودة" من املطابقة الدقيقة. •
بالنظر إلى الوسيطة األولى فقط ،فإن ) f(int، intأفضل تطابق من ).f(double، double •
عندما ننظر إلى الوسيطة الثانية ،فإن) f(double، doubleهي مطابقة تامة للوسيطة .2.56 •
يتطلب استدعاء ) f(int، intتحويل 2.56من doubleإلى .int •
عندما نفكر في املعلمة الثانية فقط ،فإن الدالة ) f(double، doubleهي أفضل تطابق. •
أفضل املمارسات
ينبغي أال تكون هناك حاجة إلى القولبة الستدعاء دالة زائدة التعبئة.
حيث تشري إلى أن مجموعات املعلمات صممت بشكل سئي.
التمرين :6.49
-ما هي الدالة املرشحة؟ وما هي الدالة القابلة للتطبيق؟
التمرين :6.50
-بالنظر إلى اإلعالنات الخاصة بـ ، fقم بإدراج الدوال القابلة للتطبيق ،إن وجدت ،لكل من االستدعاءات التالية.
-حدد الدالة األفضل تطاب ًقا ،أو إذا كان اﻻستدعاء غري رشعياً سواء بعدم وجود تطابق أو بسبب الغموض في
اﻻستدعاء.
)(a )f(2.56, 42
)(b )f(42
)(c )f(42, 0
)(d )f(2.56, 3.14
التمرين :6.51
-اكتب كل النسخ األربعة لـ .fيجب أن تطبع كل دالة رسالة ممزية.
-تحقق من إجاباتك للتمرين السابق.
-إذا كانت إجاباتك غري صحيحة ،قم بدراسة هذا القسم حىت تفهم سبب خطأ إجاباتك.
من أجل تحديد أفضل تطابق ،يقوم املرتجم بتصنيف التحويالت اليت يمكن استخدامها لتحويل كل وسيطة إلى نوع
معلمة مقابلة لها .يتم تصنيف التحويالت على النحو التالي:
.1تطابق تام .تحدث املطابقة التامة عندما:
• تكون الوسيطة وأنواع املعلمات متطابقة.
• يتم تحويل الوسيطة من نوع مصفوفة أو دالة إلى نوع املؤرش املقابل.
• يتم إضافة قيمة ثابتة عالية املستوى إلى الوسيطة أو تجاهلها.
.2تطابق من خالل تحويل ثابت.
.3تطابق من خالل ترقية .
.4تطابق من خالل عملية حسابية أو تحويل مؤرش.
.5تطابق من خالل تحويل من نوع الفئة.
تحذير
• يمكن أن تأتي الرتقية والتحويالت بني األنواع املدمجة بنتائج مفاجئة في سياق مطابقة الدوال.
نادرا ما تتضمن األنظمة املصممة جي ًدا دوال ذات معلمات وثيقة الصلة مثل
ً • لكن لحسن الحظ ،
تلك املوجودة في األمثلة التالية.
;)void ff(int
;)void ff(short
)ff('a');// char promotes to int; calls f(int
يتم التعامل مع جميع التحويالت الحسابية على أنها مكافئة لبعضها. •
التحويل من intإلى intبال_إشارة ، •
على سبيل املثال ،التحويل من intإلى doubleال يحظى بأولوية .كمثال ملموس ،فكر في : •
;)void manip(long
;)void manip(float
manip(3.14); // error: ambiguous call
قيمة الحرفية 3.14عبارة عن .doubleيمكن تحويل هذا النوع إلى نوع longأو .float •
ونظر ًا لوجود تحويلني حسابيني محتملني ،فسيعد االستدعاء غامضاً. •
عندما نستدعي دالة زائدة التعبئة تختلف فيما إذا كانت معلمة مرجع أو مؤرش لثابت ،يستخدم املرتجم ثبات
الوسيطة لتحديد الدالة اليت يجب استدعاءها :
في أول استدعاء ،نمرر الكائن الثابت .aال يمكننا ربط مرجع عادي بكائن ثابت. •
مرجعا لثابت.
ً في هذه الحالة ،تكون الدالة الوحيدة القابلة للتطبيق هي اإلصدار الذي يأخذ •
عالوة على ذلك ،فإن هذا االستدعاء هو مطابقة تامة للوسيطة .a •
في االستدعاء الثاني ،نمرر كائن غري ثابت .bلهذا اﻻستدعاء ،كلتا الدالتني قابلة للتطبيق. •
يمكننا استخدام bلتهيئة مرجع لثابت أو غري ثابت. •
مع ذلك ،فإن تهيئة مرجع لثابت على كائن غري ثابت يتطلب تحويالً. •
اإلصدار الذي يأخذ معلمة غري ثابت هو مطابقة تامة لـ .bومن ثم ،يفضل اإلصدار غري الثابت. •
التمرين :6.52
بالنظر إلى اﻹعالنات التالية ،
;)void manip(int, int
;double dobj
ما هو التصنيف لكل تحويل في اﻻستدعاءات التالية؟
;)'(a) manip('a', 'z
;)(b) manip(55.4, dobj
:6.53 التمرين
. ارشح تأثري اإلعالن الثاني في كل مجموعة من مجموعات اإلعالنات التالية-
. إن وجد، حدد أيها غري رشعي-
(a) int calc(int&, int&);
int calc(const int&, const int&);
(b) int calc(char*, char*);
int calc(const char*, const char*);
(c) int calc(char*, char*);
int calc(char* const, char* const);
6٫7الـمؤشــــــــــرات للــــــدوال
6.7. Pointers to Functions
عبارة عن دالة تملك نوعاً منطقياً (مرجع لسلسلة ثابتة ،مرجع لسلسلة ثابتة). •
ولإلعالن عن مؤرش يمكن أن يشري لهذه الدالة ،نعلن عن مؤرش مكان اسم الدالة : •
// pf points to function returning bool takes two const string references
;)& bool (*pf)(const string &, const string // uninitialized
ملحوظة
عالوة على ذلك ،يمكننا استخدام مؤرش دالة الستدعاء دالة يشري إليها املؤرش.
: ليست هناك حاجة إللغاء مرجع املؤرش- يمكننا القيام بذلك مبارشة
.ال يوجد تحويل بني مؤرشات نوع دالة ومؤرشات نوع دالة أخرى •
مقيم كصفر ملؤرش دالة لنشري
ّ أو تعبري ثابت لعدد صحيحnullptr يمكننا تعيني، كالعادة، مع ذلك •
: إلى أن املؤرش ال يشري إلى أي دالة
. يجب أن يوضح السياق أي إصدار يتم استخدامه، عندما نستخدم دالة زائدة التعبئة، كالعادة
وعندما نعلن عن مؤرش لدالة زائدة التعبئة
void ff(int*);
void ff(unsigned int);
void (*pf1)(unsigned int) = ff; // pf1 points to ff(unsigned)
.يستخدم املرتجم نوع املؤرش لتحديد الدالة زائدة التعبئة اليت يجب استخدامها
:تماما مع إحدى الدوال زائدة التعبئة
ً يجب أن يتطابق نوع املؤرش
كما رأينا للتو في إعالن ، useBiggerفإن كتابة أنواع مؤرشات الدوال رسعان ما تصبح مملة. •
فأسماء النوع املستعارة ،جنبًا إلى جنب مع النوع-املعلن ،تسمح لنا بتبسيط كود يستخدم مؤرشات دالة : •
كحال املصفوفات ،ال يمكننا إعادة نوع دالة ولكن يمكننا إعادة مؤرش لنوع دالة.
وباملثل ،يجب أن نكتب نوع اإلعادة كنوع مؤرش ؛ •
تلقائيا مع نوع إعادة دالة كنوع مؤرش مقابل.
ً لن يتعامل املرتجم •
أيضا وكما هو الحال مع عمليات إعادة املصفوفة ،
ً •
فإن أسهل طريقة لإلعالن عن دالة تعيد مؤرشًا لدالة هي باستخدام اسم نوع مستعار : •
استخدمنا هنا إعالنات اسم نوع مستعار لتعريف Fكنوع دالة و PFكمؤرش لنوع الدالة. •
اليشء الذي يجب مراعاته هو أنه ،على عكس ما يحدث للمعلمات اليت لها نوع دالة ، •
تلقائيا إلى نوع املؤرش.
ً ال يتم تحويل نوع اإلعادة •
يجب أن نحدد رصاحة أن نوع اإلعادة هو نوع املؤرش: •
أيضا
أيضا اإلعالن عن f1مبارش ًة ،وهو ما سنفعله ً
بالطبع ،يمكننا ً
عند قراءة هذا اإلعالن من الداخل إلى الخارج ،نرى أن f1تملك قائمة معلمات ،لذا فهي دالة. •
يسبق * f1لذا تعيد مؤرشًا .يملك نوع هذا املؤرش نفسه قائمة معلمات ،لذا يشري لدالة. •
هذه الدالة تعيد .int •
من أجل االكتمال ،تجدر اإلشارة إلى أنه يمكننا تبسيط إعالنات دوال تعيد مؤرشات لتعمل باستخدام العائد
املتأخر :
إذا كنا نعرف الدالة (الدوال) اليت نريد إعادتها ،فيمكننا استخدام النوع املعلن لتبسيط كتابة نوع إعادة مؤرش دالة.
على سبيل املثال ،لنفرتض أن لدينا دالتني ،كلتاهما تعيد سلسلة string::size_type •
ولديها سلسلتان ثابتتان ومعلمات. •
يمكننا كتابة دالة ثالثة تأخذ معلمة سلسلة وتعيد مؤرشًا إلحدى الدالتني على النحو التالي: •
الجزء الصعب الوحيد في اإلعالن عن getFcnهو أن نتذكر أنه عندما نطبق نوع decltypeعلى دالة ، •
فإنه يُعيد نوع دالة ،وليس مؤرشًا لنوع دالة. •
يجب أن نضيف * لإلشارة إلى أننا نعيد مؤرشًا وليس دالة. •
التمرين :6.54
-اكتب إعالنا لدالة تأخذ معلمتني من معامالت intوتقوم بإعادة ، int
-وقم بتعريف املتجه الذي تحتوي عنارصه على نوع مؤرش الدالة هذا.
التمرين :6.55
-اكتب أربع دوال تجمع بني قيميت intوتطرحهما وترضبهما وتقسمهما.
-قم بتخزين مؤرشات إلى هذه القيم في املتجه من التمرين السابق.
التمرين :6.56
استدع كل عنرص في املتجه واطبع نتيجته.
ملخــــــــــص الفصــــــــــل
Chapter Summary
تحتوي كل دالة على نوع إعادة ،واسم ،وقائمة (قد تكون فارغة) من املعلمات ،وبنية دالة.
بنية الدالة عبارة عن كتلة يتم تنفيذها عند استدعاء الدالة. •
arguments وسيطات :قيم مقدمة في استدعاء دالة يتم استخدامها لتهيئة معلمات الدالة.
توكيد :ماكرو معالج أولي يأخذ تعبريًا واح ًدا ،يستخدمه كرشط.
assert عندما ال يتم تعريف متغري املعالج اﻷولي ، NDEBUGيقوم التوكيد بتقييم الحالة ،
فإذا كان الرشط خاطًئ ا ،يكتب رسالة وينهي الربنامج.
أفضل تطابق :دالة مطابقة تم تحديدها من مجموعة دوال زائدة التعبئة الستدعاء ما.
في حالة وجود أفضل تطابق ،تكون الدالة املحددة أفضل تطابق من جميع املرشحات
best match
األخرى القادرة على االستمرار في وسيطة واحدة على األقل في اﻻستدعاء وليست أسوأ
من بقية الوسيطات.
call by
reference استدعاء بواسطة مرجع :انظر تمرير بواسطة مرجع.
دوال مرشحة :مجموعة من دوال يتم النظر فيها عند حل استدعاء دالة.
candidate
functions الدوال املرشحة هي جميع الدوال ذات االسم املستخدم في استدعاء يكون اإلعالن في
نطاقها وقت اﻻستدعاء.
function مطابقة دالة :عملية للمرتجم يتم من خاللها حل استدعاء دالة زائدة التعبئة.
matching تتم مقارنة الوسيطات املستخدمة في االستدعاء بقائمة معلمات كل دالة زائدة التعبئة.
نموذج دالة أولي :إعالن دالة ،يتكون من االسم ونوع اإلعادة وأنواع املعلمات الخاصة
function
prototype بالدالة.
الستدعاء دالة ،يجب أن يكون قد تم اإلعالن عن نموذجها األولي قبل نقطة االستدعاء.
أسماء مخفية :أسماء معلنة داخل نطاق تخفي الكيانات املعلنة مسب ًقا بنفس األسماء
hidden names
املعلنة خارج هذا النطاق.
inline دالة ضمن-السطر :طلب من املرتجم أن يوسع دالة عند نقطة االستدعاء ،
function إن أمكن .تتجنب الدوال املضمنة الحمل العادي الستدعاء الدوال.
local
variables متغريات محلية :متغريات معرفة داخل كتلة.
ال تطابق :خطأ في وقت الرتجمة ينتج أثناء مطابقة الدالة عند عدم وجود دالة ذات
no match
معلمات تطابق وسيطات استدعاء معني.
object code كود كائن :يحول املرتجم إليه كود املصدر الخاصة بنا.
ملف كائن :يحتوي امللف على كود كائن تم إنشاؤه بواسطة املرتجم من ملف مصدر
object file معني.
معا.
يتم إنشاء ملف قابل للتنفيذ من ملف كائن واحد أو أكرث بعد ربط امللفات ً
overload
resolution زيادة تعبئة دقة أو قرار :انظر مطابقة دالة.
overloaded دالة زائدة التعبئة :دالة لها نفس اسم دالة أخرى على األقل.
function يجب أن تختلف الدوال زائدة التعبئة في عدد أو نوع معلماتها.
تمرير بواسطة مرجع :وصف كيفية تمرير وسيطات إلى معلمات نوعها مرجعي.
pass by
reference تعمل املعلمات املرجعية بنفس الطريقة اليت تعمل بها أي استخدام آخر للمراجع ؛
كمعلمة مرتبطة بالوسيطة املقابلة لها.
تمرير بواسطة قيمة :كيفية تمرير وسيطة إلى معلمات من نوع غري مرجعي.
pass by value
املعلمة غري املرجعية هي نسخة من قيمة الوسيطة املقابلة لها.
ماكرو معالج أولي :قسم معالج أولي يترصف مثل دالة ضمن-السطر.
preprocessor
macro برصف النظر عن التوكيد ،ال تستخدم برامج يس ++الحديثة سوى القليل ج ًدا من وحدات
ماكرو املعالج األولي.
recursive
function دالة استدعاء ذاتي :تستدعي نفسها بشكل مبارش أو غري مبارش.
return type نوع إعادة :جزء من تعريف دالة يحدد نوع قيمة تعيدها الدالة.
separate
compilation تجميع منفصل :القدرة على تقسيم الربنامج إلى عدة ملفات مصدر منفصلة.
trailing
return type نوع إعادة متأخر :نوع إعادة محدد بعد قائمة املعلمات.
دوال قابلة للتطبيق :مجموعة فرعية من دوال مرشحة يمكن أن تتطابق مع استدعاء
viable معني.
functions تحتوي الدوال القابلة للتطبيق على نفس عدد املعلمات مثل وسيطات االستدعاء ،
ويمكن تحويل كل نوع وسيطة إلى نوع املعلمة املقابل.