You are on page 1of 68

‫الفصــــــــــل ‪ .

6‬الــــــــــدوال‬
‫‪Chapter 6. Functions‬‬

‫الـمحتويات‬
‫‪Contents‬‬

‫‪6.1 Function Basics‬‬ ‫‪ 6٫1‬أساسيــــــات الــــــدالة‬

‫‪6.2 Argument Passing‬‬ ‫‪ 6٫2‬تمــــــرير وسيــــــطة‬

‫‪6.3 Return Types and return Statement‬‬ ‫‪ 6٫3‬أنــــــواع اﻹعادة والجمــــــلة ِ‬


‫أعــــــد‬

‫‪6.4 Overloaded Functions‬‬ ‫‪ 6٫4‬الــــــدوال زائــــــدة التعبئــــــة‬

‫‪6.5 Features for Specialized Uses‬‬ ‫‪ 6٫5‬الـمـــزيات لالستـــخدامات املتخصصـــة‬

‫‪6.6 Function Matching‬‬ ‫‪ 6٫6‬مطــــــابقة الــــــدالــــــة‬

‫‪6.7 Pointers to Functions‬‬ ‫‪ 6٫7‬الـمؤشــــــرات لــــــدوال‬

‫‪Chapter Summary‬‬ ‫ملخــــــص الفصــــــل‬

‫‪Defined Terms‬‬ ‫مصطلحــــــات معرفــــــة‬

‫يصف هذا الفصل ‪:‬‬


‫كيفية تعريف الدوال واإلعالن عنها‪ .‬سنغطي كيفية تمرير وسيطات إليها وكيفية إعادة قيم منها‪.‬‬ ‫•‬
‫في يس‪ ، ++‬يمكن للدوال أن تكون زائدة التعبئة ‪ ،‬ما يعين إمكانية استخدام نفس االسم لعدة دوال مختلفة‪.‬‬ ‫•‬
‫سنغطي كيفية زيادة تعبئة الدوال ‪،‬‬ ‫•‬
‫وكيفية اختيار املرتجم لإلصدار املطابق الستدعاء محدد من عدة دوال متعددة‪.‬‬ ‫•‬
‫يُختتم الفصل بوصف مؤرشات للدوال‪.‬‬ ‫•‬

‫اسما‪.‬‬
‫ً‬ ‫الدالة عبارة عن كتلة من أكواد تحمل‬
‫نقوم بتنفيذ تلك األكواد عن طريق استدعاء الدالة‪.‬‬ ‫•‬
‫قد تأخذ الدالة صفرًا أو أكرث من الوسيطات (عاد ًة) ينتج عنها نتيجة‪.‬‬ ‫•‬
‫يمكن زيادة تعبئة الدوال ‪ ،‬ما يعين إمكانية أن يشري نفس االسم إلى عدة دوال مختلفة‪.‬‬ ‫•‬
‫‪ 6٫1‬أســــــــــاسيات الــــــدالــــــة‬
‫‪6.1. Function Basics‬‬

‫يتكون تعريف الدالة عاد ًة من نوع إعادة ‪ ،‬واسم ‪ ،‬وقائمة صفرية أو أكرث من املعلمات ‪ ،‬وبنية‪.‬‬
‫يتم تحديد املعلمات في قائمة مفصولة بفواصل محاطة بأقواس‪.‬‬ ‫•‬
‫ويتم تحديد اإلجراءات اليت تؤديها الدالة في كتلة جمل‪ ،‬يشار إليها باسم بنية الدالة‪.‬‬ ‫•‬
‫نقوم بتنفيذ دالة من خالل عامل االستدعاء ‪ ،‬وهو زوج من األقواس‪.‬‬ ‫•‬
‫يأخذ عامل اﻻستدعاء تعبريًا عبارة عن دالة أو يشري إلى دالة‪.‬‬ ‫•‬
‫يوجد داخل األقواس قائمة وسيطات مفصولة بفواصل‪ .‬ويتم استخدام تلك الوسيطات لتهيئة معلمات الدالة‪.‬‬ ‫•‬
‫نوع تعبري االستدعاء هو نوع إعادة الدالة‪.‬‬ ‫•‬

‫‪Writing a Function‬‬ ‫كتابة دالة‬

‫كمثال ‪،‬‬
‫سنكتب دالة لتقرير املرضوب لرقم معني‪.‬‬ ‫•‬
‫املرضوب للرقم ‪ n‬هو حاصل رضب األرقام من ‪ 1‬إلى ‪ .n‬مرضوب ‪ - 5‬مثالً ‪ -‬هو ‪.120‬‬ ‫•‬

‫‪1 * 2 * 3 * 4 * 5 = 120‬‬

‫نعرف هذه الدالة على النحو التالي‪:‬‬


‫قد ّ‬
‫)‪// factorial of val is val *(val-1) *(val-2) ... *((val-(val-1)) *1‬‬
‫)‪int fact(int val‬‬
‫{‬
‫‪int ret = 1; // local variable to hold result as we calculate it‬‬
‫)‪while (val > 1‬‬
‫;‪ret *= val--‬‬ ‫‪// assign ret * val to ret and decrement val‬‬
‫;‪return ret‬‬ ‫‪// return the result‬‬
‫}‬

‫تسمى دالتنا ‪.fact‬‬ ‫•‬


‫تأخذ ‪ fact‬معلمة عدد صحيح واحدة وتعيد قيمة عدداً صحيحاً‪.‬‬ ‫•‬
‫داخل حلقة طالـما_ ‪ ،‬نحوسب املرضوب باستخدام عامل تناقص الحقة لتقليص ‪ 1‬من ‪ val‬مع كل تكرار‪.‬‬ ‫•‬
‫تنهي الجملة ‪ return‬تنفيذ ‪ fact‬وتعيد قيمة ‪.ret‬‬ ‫•‬

‫‪Calling a Function‬‬ ‫استدعاء دالة‬

‫أيضا عدداً صحيحاً ‪:‬‬


‫الستدعاء ‪ ، fact‬يجب علينا توفري قيمة عدد صحيح‪ .‬نتيجة االستدعاء ستكون ً‬

‫)(‪int main‬‬
‫{‬
‫;)‪int j = fact(5‬‬ ‫)‪// j equals 120, i.e., result of fact(5‬‬
‫;‪cout << "5! is " << j << endl‬‬
‫;‪return 0‬‬
‫}‬

‫يقوم استدعاء الدالة بأمرين ‪:‬‬


‫أحدهما تهيئة معلمات الدالة على وسيطاتها املقابلة ‪،‬‬ ‫•‬
‫والثاني نقل التحكم إلى تلك الدالة‪.‬‬ ‫•‬
‫سيتم تعليق تنفيذ دالة االستدعاء ويبدأ تنفيذ الدالة اليت تم استدعاؤها‪.‬‬ ‫•‬

‫يبدأ تنفيذ الدالة بالتعريف (الضمين) وتهيئة معلماتها‪.‬‬


‫بالتالي ‪ ،‬عندما نستدعي ‪ ، fact‬فإن أول يشء يحدث هو إنشاء متغري عدد صحيح يسمى ‪.val‬‬ ‫•‬
‫تتم تهيئة هذا املتغري على الوسيطة في استدعاء ‪ ، fact‬واليت في هذه الحالة هي الرقم خمسة‪.‬‬ ‫•‬
‫ينتهي تنفيذ الدالة عند مصادفة الجملة ِ‬
‫أعد ‪.return‬‬ ‫•‬
‫ومثل استدعاء دالة ‪ ،‬فإن جملة ِ‬
‫أعد تقوم بأمرين ‪:‬‬ ‫•‬
‫أحدهما إعادة قيمة (إن وجدت) في ِ‬
‫أعد ‪،‬‬ ‫•‬
‫والثاني نقل التحكم خارج الدالة املستدعاة مرة أخرى نحو دالة االستدعاء‪.‬‬ ‫•‬
‫تُستخدم القيمة املعادة من الدالة لتهيئة نتيجة تعبري االستدعاء‪.‬‬ ‫•‬
‫سيستمر التنفيذ مع ما تبقى من التعبري الذي ظهر فيه اﻻستدعاء‪.‬‬ ‫•‬

‫وبالتالي ‪ ،‬فإن استدعاءنا لـ ‪ fact‬يعادل ما يلي‪:‬‬

‫;‪int val = 5‬‬ ‫‪// initialize val from literal 5‬‬


‫;‪int ret = 1‬‬ ‫‪// code from body of fact‬‬
‫)‪while (val > 1‬‬
‫;‪ret *= val--‬‬
‫;‪int j = ret‬‬ ‫‪// initialize j as copy of ret‬‬

‫‪Parameters and Arguments‬‬ ‫املعلمات و الوسيطات‬

‫الوسيطات هي مهيئات ملعلمات الدالة‪.‬‬


‫تقوم أول وسيطة بتهيئة أول معلمة ‪ ،‬وتهئي الوسيطة الثانية املعلمة الثانية ‪ ،‬وهكذا‪.‬‬ ‫•‬
‫نعرف الوسيطة اليت تهئي أي متغري ‪ ،‬إﻻ أننا ﻻ نملك ضمانات حول الرتتيب الذي يتم‬ ‫وبالرغم من أننا ّ‬ ‫•‬
‫تقييم الوسيطات به‪.‬‬
‫للمرتجم كامل الحرية في تقييم الوسيطات بأي ترتيب يفضله‪.‬‬ ‫•‬
‫يجب أن يتطابق نوع كل وسيطة مع نوع معلمته املقابلة ؛‬ ‫•‬
‫بنفس الطريقة اليت يجب أن يتطابق بها نوع أي ُمهئي مع نوع الكائن الذي يتم تهيئته‪.‬‬ ‫•‬
‫يجب أن نمرر بالضبط نفس عدد الوسيطات مثل عدد معلمات الدالة‪.‬‬ ‫•‬
‫ونظرًا ألن كل استدعاء يضمن تمرير عدة وسيطات بعدد ما لدى الدالة من معلمات ‪،‬‬ ‫•‬
‫دائما تهيئة املعلمات‪.‬‬
‫ً‬ ‫يتم‬ ‫•‬
‫وألن لدى ‪ fact‬معلمة عدد صحيح واحدة ‪،‬‬ ‫•‬
‫ففي كل مرة نستدعيها يجب علينا توفري وسيطة واحدة يمكن تحويلها لعدد صحيح ‪:‬‬ ‫•‬

‫;)"‪fact("hello‬‬ ‫‪// error: wrong argument type‬‬


‫;)(‪fact‬‬ ‫‪// error: too few arguments‬‬
‫;)‪fact(42, 10, 0‬‬ ‫‪// error: too many arguments‬‬
‫;)‪fact(3.14‬‬ ‫‪// ok: argument is converted to int‬‬

‫فشل أول استدعاء لعدم وجود تحويل من حرفي ثابت إلى عدد صحيح‪.‬‬ ‫•‬
‫مررت االستدعاءات الثانية والثالثة عد ًدا خاطًئ ا من الوسيطات‪.‬‬ ‫•‬
‫وﻷنه يجب استدعاء دالة ‪ fact‬بوسيطة واحدة ؛‬ ‫•‬
‫فمن الخطأ استدعاءها بأي عدد آخر‪.‬‬ ‫•‬
‫اﻻستدعاء األخري رشعي ألنه يوجد تحويل من املزدوج العرشي إلى عدد صحيح‪.‬‬ ‫•‬
‫ضمنيا إلى عدد صحيح (من خالل االقتطاع)‪.‬‬
‫ً‬ ‫في هذا االستدعاء ‪ ،‬يتم تحويل الوسيطة‬ ‫•‬
‫بعد التحويل ‪ ،‬سيعادل هذا اﻻستدعاء‬ ‫•‬

‫;)‪fact(3‬‬

‫‪Function Parameter List‬‬ ‫قائمة معلمات الدالة‬

‫يمكن أن تكون قائمة معلمات الدالة فارغة لكن ال يمكن حذفها‪.‬‬


‫عاد ًة ما نعرف دالة بدون معلمات عن طريق كتابة قائمة معلمات فارغة‪.‬‬ ‫•‬
‫أيضا استخدام كلمة ‪ void‬لنشري لعدم وجود معلمات‪:‬‬
‫وللتكامل مع لغة اليس ‪ ،‬يمكن ً‬ ‫•‬

‫} ‪void f1(){ /* ... */‬‬ ‫‪// implicit void parameter list‬‬


‫} ‪void f2(void){ /* ... */‬‬ ‫‪// explicit void parameter list‬‬

‫تتكون قائمة املعلمات عاد ًة من قائمة معلمات مفصولة بفواصل ‪،‬‬ ‫•‬
‫بـمع ِلن واحد‪.‬‬
‫كل منها يبدو كإعالن ُ‬ ‫•‬
‫فحىت في حالة تطابق نوعي املعلمتني ‪ ،‬يجب تكرار كتابة النوع ‪:‬‬ ‫•‬

‫} ‪int f3(int v1, v2) { /* ... */‬‬ ‫‪// error‬‬


‫} ‪int f4(int v1, int v2) { /* ... */‬‬ ‫‪// ok‬‬

‫ال يمكن أن تحمل معلمتان االسم نفسه‪ .‬عالوة على ذلك ‪،‬‬ ‫•‬
‫ال يسمح أن تستخدم متغريات محلية في النطاق الخارجي للدالة نفس االسم مثل أي معلمة‪.‬‬ ‫•‬

‫أسماء املعلمات اختيارية‪ .‬مع ذلك ‪ ،‬ال توجد طريقة الستخدام معلمة غري مسماة‪.‬‬
‫لذلك ‪ ،‬عاد ًة ما يكون للمعلمات أسماء‪.‬‬ ‫•‬
‫ومن حني آلخر ‪ ،‬تحتوي الدالة على معلمة غري مستخدمة‪.‬‬ ‫•‬
‫غالبا ما تُرتك مثل هذه املعلمات بدون تسمية ‪ ،‬لإلشارة إلى عدم استخدامها‪.‬‬
‫ً‬ ‫•‬
‫ال يؤدي ترك املعلمة بدون اسم إلى تغيري عدد الوسيطات اليت يجب أن يوفرها اﻻستدعاء‪.‬‬ ‫•‬
‫يجب أن يوفر االستدعاء وسيطة لكل معلمة ‪ ،‬حىت إن لم يتم استخدام تلك املعلمة‪.‬‬ ‫•‬

‫‪Function Return Type‬‬ ‫نوع إعادة الدالة‬

‫يمكن استخدام معظم األنواع كنوع إعادة للدالة‪.‬‬


‫وباﻷخص ‪ ،‬يمكن أن يكون نوع اإلعادة ‪ ، void‬ما يعين أن الدالة ال تعيد قيمة‪.‬‬ ‫•‬
‫مع ذلك ‪ ،‬ال يسمح أن يكون نوع اإلعادة نوع مصفوفة أو نوع دالة‪.‬‬ ‫•‬
‫ومع ذلك ‪ ،‬قد تعيد الدالة مؤرشًا ملصفوفة أو دالة‪.‬‬ ‫•‬
‫سرنى كيفية تعريف دوال تعيد مؤرشات (أو مراجع) ملصفوفات في الفقرة ‪6.3.3‬‬ ‫•‬
‫وكيفية إعادة مؤرشات لدوال في الفقرة ‪. 6.7‬‬ ‫•‬

‫تمارين القسم ‪6.1‬‬


‫تمرين ‪:6.1‬‬
‫ما هو الفرق بني املعلمة والوسيطة؟‬

‫التمرين ‪: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‬لوسيطتها‪.‬‬

‫‪6.1.1. Local Objects‬‬ ‫‪ 6٫1٫1‬الكائنات املحلية‬

‫في لغة يس‪ ، ++‬تملك األسماء نطاقاً ‪ ،‬وتملك الكائنات ُع ْمراً‪ .‬من املهم فهم هذين املفهومني‪.‬‬
‫فنطاق االسم هو جزء من بنية الربنامج يظهر فيه هذا االسم‪.‬‬ ‫•‬
‫أما ُع ْم ر الكائن فهو الوقت الذي يوجد فيه ذلك الكائن أثناء تنفيذ الربنامج‪.‬‬ ‫•‬

‫وكما رأينا ‪ ،‬بنية الدالة عبارة عن كتلة جمل‪.‬‬


‫كالعادة ‪ ،‬تشكل الكتلة نطا ًقا جدي ًدا يمكننا من خالله تعريف متغريات‪.‬‬ ‫•‬
‫يشار إلى املعلمات واملتغريات املعرفة داخل بنية دالة باملتغريات املحلية‪.‬‬ ‫•‬
‫إنها "محلية" لهذه الدالة وتخفي اإلعالنات اليت تحمل االسم نفسه في نطاق خارجي آخر‪.‬‬ ‫•‬
‫الكائنات املعرفة خارج أي دالة تتواجد خالل تنفيذ الربنامج‪.‬‬ ‫•‬
‫يتم إنشاء هذه الكائنات عند بدء تشغيل الربنامج وال يتم إتالفها حىت ينتهي الربنامج‪.‬‬ ‫•‬
‫يعتمد عمر املتغري املحلي على كيفية تعريفه‪.‬‬ ‫•‬

‫‪Automatic Objects‬‬ ‫كائنات تلقائية‬

‫يتم خلق الكائنات اليت تتوافق مع املتغريات املحلية العادية عندما يمر مسار تحكم الدالة عرب تعريف املتغري‪.‬‬
‫حيث يتم تدمريها عندما يمر عنرص التحكم عرب نهاية الكتلة اليت تم تعريف املتغري فيها‪.‬‬ ‫•‬
‫تُعرف الكائنات اليت تتواجد فقط أثناء تنفيذ كتلة بالكائنات التلقائية ‪.Automatic Objects‬‬ ‫•‬
‫معرفة‪.‬‬
‫بعد أن يخرج التنفيذ من كتلة ‪ ،‬تكون قيم الكائنات التلقائية اليت أنشئت في تلك الكتلة غري ّ‬ ‫•‬

‫املعلمات عبارة عن كائنات تلقائية‪.‬‬


‫يتم تخصيص التخزين للمعلمات عندما تبدأ الدالة‪.‬‬ ‫•‬
‫ُعرف املعلمات في نطاق بنية الدالة‪ .‬ومن ثم يتم تدمريها عند إنهاءها‪.‬‬
‫ت َّ‬ ‫•‬

‫تتم تهيئة الكائنات التلقائية املقابلة ملعلمات الدالة على الوسيطات اليت يتم تمريرها إلى الدالة‪.‬‬
‫يتم تهيئة الكائنات التلقائية املقابلة على املتغريات املحلية إذا كان تعريفها يحتوي على ُمهئي‪.‬‬ ‫•‬
‫عدا ذلك ‪ ،‬سيتم تهيئتها بشكل افرتايض ‪ ،‬ما يعين أن املتغريات املحلية غري املهيأة من النوع املدمج لها قيم‬ ‫•‬
‫معرفة‪.‬‬
‫غري ّ‬

‫‪Local static 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‬‬

‫سيقوم هذا الربنامج بطباعة األرقام من ‪ 1‬إلى ‪ 10‬ضمناً‪.‬‬ ‫•‬


‫قبل أن يتدفق التحكم عرب تعريف ‪ ctr‬ألول مرة ‪ ،‬يتم إنشاء ‪ ctr‬وإعطاءه القيمة األولية ‪.0‬‬ ‫•‬
‫كل استدعاء يزيد ‪ ctr‬ويعيد قيمته الجديدة‪.‬‬ ‫•‬
‫عندما يتم تنفيذ ‪ ، count_calls‬فإن املتغري ‪ ctr‬موجود بالفعل وله قيمة أيا تكن في هذا املتغري في‬ ‫•‬
‫آخر مرة تم فيها إنهاء الدالة‪.‬‬
‫وهكذا ‪ ،‬في اﻻستدعاء الثاني ‪ ،‬تكون قيمة ‪ ctr‬هي ‪ ، 1‬وفي الثالث تساوي ‪ ، 2‬وهكذا‪.‬‬ ‫•‬
‫إذا لم يكن للساكن املحلي ُمهئي رصيح ‪ ،‬فسيتم تهيئته ‪،‬‬ ‫•‬
‫ما يعين أن الساكن املحلي من النوع املدمج يتم تهيئته على الصفر‪.‬‬ ‫•‬

‫تمارين القسم ‪6.1.1‬‬


‫تمرين ‪:6.6‬‬
‫‪ -‬ارشح الفروق بني املعلمة واملتغري املحلي واملتغري املحلي الساكن‪.‬‬
‫‪ -‬أعط مثاال ً لدالة قد يكون كل منها مفي ًدا‪.‬‬
‫التمرين ‪:6.7‬‬
‫‪ -‬اكتب دالة تقوم بإعادة ‪ 0‬عند استدعائها ألول مرة‬
‫‪ -‬ثم تقوم بإنشاء أرقام متسلسلة في كل مرة يتم استدعاؤها مرة أخرى‪.‬‬

‫‪6.1.2. Function Declarations‬‬ ‫‪ 6٫1٫2‬إعالنات دالة‬

‫مثل أي اسم آخر ‪ ،‬يجب اإلعالن عن اسم الدالة قبل أن نتمكن من استخدامها‪.‬‬
‫وكما هو حال املتغريات ‪ ،‬يمكن تعريف الدالة مرة واحدة فقط ولكن يمكن اإلعالن عنها عدة مرات‪.‬‬ ‫•‬
‫باستثناء واحد سنقوم بتغطيته في الفقرة ‪، 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‬‬
‫تُعرف إعالنات الدوال ً‬ ‫•‬

‫‪Function Declarations Go in Header Files‬‬ ‫تكون إعالنات الدالة في ملفات العنوان‬

‫ُعرف في ملفات املصدر‪.‬‬


‫تذكر أنه يعلن عن املتغريات في ملفات العنوان وت ّ‬
‫لنفس األسباب ‪ ،‬يجب اﻹعالن عن الدوال في ملفات العنوان وتعريفها في امللفات املصدر‪.‬‬ ‫•‬
‫قد يكون من املغري ‪ -‬وسيكون رشعياً ‪ -‬وضع إعالن الدالة مبارشة في كل ملف مصدر يستخدم الدالة‪.‬‬ ‫•‬
‫مع ذلك ‪ ،‬يعد القيام بذلك أمراً شاقاً وعرض ًة للخطأ‪.‬‬ ‫•‬

‫عندما نستخدم ملفات العنوان إلعالنات دوالنا ‪،‬‬


‫يمكننا التأكد من أن جميع اإلعالنات الخاصة بدالة معينة تتفق‪.‬‬ ‫•‬
‫عالوة على ذلك ‪ ،‬إذا تغريت واجهة الدالة ‪ ،‬فيجب تغيري إعالن واحد فقط‪.‬‬ ‫•‬
‫يعرف إحدى الدوال على العنوان الذي يحتوي على إعالن هذه الدالة‪.‬‬
‫يجب أن يشتمل امللف املصدر الذي ّ‬ ‫•‬
‫بهذه الطريقة سيتحقق املرتجم من أن التعريف واإلعالن متسقان‪.‬‬ ‫•‬

‫أفضل املمارسات‬

‫يعرف هذه الدالة‪.‬‬


‫يجب تضمني العنوان الذي يعلن عن دالة في امللف املصدر الذي ّ‬

‫تمارين القسم ‪6.1.2‬‬


‫التمرين ‪:6.8‬‬
‫اكتب ملف عنوان باسم ‪ Chapter6.h‬يحتوي على إعالنات للدوال اليت كتبتها للتدريبات في الفقرة ‪. 6.1‬‬

‫‪6.1.3. Separate Compilation‬‬ ‫‪ 6٫1٫3‬التجميع املنفصل‬

‫نظرًا ألن برامجنا تزداد تعقي ًدا ‪ ،‬فسرنغب في تخزين أجزاء الربنامج املختلفة في ملفات منفصلة‪.‬‬
‫• مثالً ‪ ،‬قد نقوم بتخزين الدوال اليت كتبناها للتمارين الواردة في الفقرة ‪ 6.1‬في ملف واحد وتخزين الكود‬
‫الذي يستخدم هذه الدوال في ملفات مصدر أخرى‪.‬‬
‫عموما بالتجميع املنفصل‪.‬‬
‫ً‬ ‫• للسماح لكتابة الربامج بأجزاء منطقية ‪ ،‬تدعم لغة يس‪ ++‬ما يُعرف‬
‫يتيح لنا التجميع املنفصل تقسيم برامجنا إلى عدة ملفات ‪ ،‬يمكن تجميع كل منها بشكل مستقل‪.‬‬ ‫•‬

‫تجميع وربط ملفات مصدر متعددة‬


‫‪Compiling and Linking Multiple Source Files‬‬

‫كمثال ‪،‬‬
‫افرتض أن تعريف دالة ‪ fact‬في ملف يسمى ‪، fact.cc‬‬ ‫•‬
‫وأن اإلعالن عنها موجود في ملف عنوان يسمى ‪.Chapter6.h‬‬ ‫•‬
‫سيحتوي ملفنا ‪ - fact.cc‬مثل أي ملف يستخدم تلك الدوال ‪ -‬على عنوان ‪.Chapter6.h‬‬ ‫•‬
‫سنخزن دالة ‪ main‬تستدعي ‪ fact‬في ملف ثانٍ باسم ‪.factMain.cc‬‬ ‫•‬
‫إلنتاج ملف قابل للتنفيذ ‪ ،‬يجب أن نخرب املرتجم بمكان العثور على كل كود نستخدمه‪.‬‬ ‫•‬
‫قد نقوم بتجميع هذه امللفات على النحو التالي‪:‬‬ ‫•‬

‫‪$ CC factMain.cc fact.cc‬‬ ‫‪# generates factMain.exe or a.out‬‬


‫‪$ CC factMain.cc fact.cc -o main‬‬ ‫‪# generates main or main.exe‬‬
‫هنا ‪ CC‬هو اسم مرتجمنا ‪ $ ،‬موجه نظامنا ‪ ،‬و ‪ #‬يبدأ تعليق سطر األوامر‪.‬‬ ‫•‬
‫يمكننا اآلن تشغيل امللف القابل للتنفيذ ‪ ،‬والذي سيقوم بتشغيل دالتنا ‪.main‬‬ ‫•‬
‫إذا قمنا بتغيري ملف واحد فقط من ملفاتنا املصدر ‪،‬‬ ‫•‬
‫فنحن نرغب في إعادة تجميع امللف الذي تم تغيريه بالفعل فقط‪.‬‬ ‫•‬
‫توفر معظم برامج الرتجمة طريقة لرتجمة كل ملف بشكل منفصل‪.‬‬ ‫•‬
‫عاد ًة ما ينتج عن هذه العملية ملف بامتداد ‪ obj.‬ـ (لـ ‪ )Windows‬أو ‪ o.‬ـ ( لـ‪، )UNIX‬‬ ‫•‬
‫ما يشري إلى أن امللف يحتوي على كود كائن ‪.object code‬‬ ‫•‬

‫معا لتشكيل ملف تنفيذي‪.‬‬


‫يتيح لنا املرتجم ربط ملفات الكائنات ً‬
‫في النظام الذي نستخدمه ‪ ،‬سنقوم بتجميع برنامجنا بشكل منفصل على النحو التالي‪:‬‬ ‫•‬

‫‪$ CC -c factMain.cc‬‬ ‫‪# generates factMain.o‬‬


‫‪$ CC -c fact.cc‬‬ ‫‪# generates fact.o‬‬
‫‪$ CC factMain.o fact.o‬‬ ‫‪# generates factMain.exe or a.out‬‬
‫‪$ CC factMain.o fact.o -o main‬‬ ‫‪# generates main or main.exe‬‬

‫ستحتاج إلى مراجعة دليل مستخدم املرتجم لفهم كيفية تجميع وتنفيذ الربامج املكونة من ملفات مصدر متعددة‪.‬‬

‫قسم التدريبات ‪6.1.3‬‬


‫التمرين ‪:6.9‬‬
‫‪ -‬اكتب نسختك الخاصة من ملفات ‪ fact.cc‬و ‪.factMain.cc‬‬
‫‪ -‬يجب أن تتضمن هذه امللفات ‪ Chapter6.h‬الخاص بك من التدريبات في القسم السابق‪.‬‬
‫‪ -‬استخدم هذه امللفات لفهم كيف يدعم املرتجم التجميع املنفصل‪.‬‬
‫‪ 6٫2‬تمــــــــــرير وســــــيطــــــة‬
‫‪6.2. Argument Passing‬‬

‫كما رأينا ‪ ،‬في كل مرة نستدعي دالة ‪ ،‬يتم إنشاء معلماتها وتهيئتها على الوسيطات اليت تم تمريرها في االستدعاء‪.‬‬

‫ملحوظة‬

‫تعمل تهيئة املعلمة بنفس طريقة تهيئة متغري‪.‬‬

‫كما هو حال أي متغري آخر ‪ ،‬يقرر نوع املعلمة التفاعل بني املعلمة ووسيطتها‪.‬‬
‫مرجعا ‪ ،‬إذن سرتتبط املعلمة بوسيطتها‪ .‬عدا ذلك ‪ ،‬يتم نسخ قيمة الوسيطة‪.‬‬
‫ً‬ ‫فإن كانت املعلمة‬ ‫•‬

‫مرجعا ‪،‬‬
‫ً‬ ‫عندما تكون املعلمة‬
‫فإننا نقول إن وسيطتها املقابلة "مررت بواسطة مرجع “‪"”passed by reference‬‬ ‫•‬
‫أو أن الدالة "استدعيت بواسطة مرجع “‪."”.called by reference‬‬ ‫•‬
‫مستعارا للكائن املرتبط بها ؛‬
‫ً‬ ‫اسما‬
‫ً‬ ‫وكما هو حال أي مرجع آخر ‪ ،‬تعترب املعلمة املرجعية‬ ‫•‬
‫أي أن املعلمة هي اسم مستعار لوسيطتها املقابلة‪.‬‬ ‫•‬

‫أما عند نسخ قيمة الوسيطة ‪ ،‬تكون املعلمة والوسيطة كائنني مستقلني‪.‬‬
‫نقول إن مثل هذه الوسيطات "مررت بواسطة قيمة “‪"”passed by value‬‬ ‫•‬
‫أو أن الدالة "استدعيت بواسطة قيمة “‪."”.called by value‬‬ ‫•‬

‫‪6.2.1. Passing Arguments by Value‬‬ ‫‪ 6٫2٫1‬تمرير وسيطات بواسطة قيمة‬

‫عندما نقوم بتهيئة متغري نوع غري مرجعي ‪ ،‬يتم نسخ قيمة املُهئي‪.‬‬
‫فال تؤثر التغيريات اليت تم إجراؤها على املتغري على املُهئي ‪:‬‬

‫;‪int n = 0‬‬ ‫‪// ordinary variable of type int‬‬


‫;‪int i = n‬‬ ‫‪// i is copy of value in n‬‬
‫;‪i = 42‬‬ ‫‪// value in i is changed; n is unchanged‬‬

‫تماما ؛‬
‫ً‬ ‫تمرير الوسيطة بواسطة قيمة يعمل بنفس الطريقة‬
‫فال يشء تفعله الدالة للمعامل يمكن أن يؤثر على الوسيطة‪ .‬مثالً ‪ ،‬داخل ‪ fact‬يتم تناقص قيمة املعلمة ‪: val‬‬

‫;‪ret *= val--‬‬ ‫‪// decrements value of val‬‬

‫وبالرغم من أن ‪ fact‬تغري قيمة ‪ ، val‬إﻻ أن هذا التغيري ليس له أي تأثري على الوسيطة املمررة إلى ‪.fact‬‬
‫فاستدعاء )‪ fact(i‬لن يغري قيمة ‪.i‬‬

‫‪Pointer Parameters‬‬ ‫معلمات املؤرش‬


‫تترصف املؤرشات كأي نوع آخر من األنواع غري املرجعية‪.‬‬
‫فعندما نقوم بنسخ مؤرش ‪ ،‬يتم نسخ قيمة املؤرش‪ .‬وبعد النسخ ‪ ،‬يكون املؤرشان متمزيان‪.‬‬ ‫•‬
‫أيضا وصوال ً غري مبارش إلى الكائن الذي يشري إليه ذلك املؤرش‪.‬‬
‫مع ذلك ‪ ،‬يمنحنا املؤرش ً‬ ‫•‬
‫يمكننا تغيري قيمة هذا الكائن عن طريق التعيني عرب املؤرش ‪:‬‬ ‫•‬

‫;‪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‬لكن وسيطة املؤرش نفسها لم تتغري‪:‬‬

‫;‪int i = 42‬‬ ‫;)‪reset(&i‬‬ ‫‪// changes i but not address of i‬‬


‫;‪cout << "i = " << i << endl‬‬ ‫‪// prints i = 0‬‬

‫أفضل املمارسات‬

‫غالبا ما يستخدم مربمجو لغة ‪ C‬معلمات املؤرش للوصول إلى كائنات خارج الدالة‪.‬‬
‫ً‬
‫أما في يس‪ ، ++‬فيستخدم املربمجون بشكل عام معلمات املرجع بدال ً من ذلك‪.‬‬

‫تمارين القسم ‪6.2.1‬‬


‫التمرين ‪:6.10‬‬
‫‪ -‬باستخدام املؤرشات ‪ ،‬اكتب دالة للتبديل بني قيميت ‪.ints‬‬
‫‪ -‬اخترب الدالة عن طريق استدعاءها وطباعة القيم املبادلة‪.‬‬

‫‪6.2.2. Passing Arguments by Reference‬‬ ‫‪ 6٫2٫2‬تمرير وسيطات بواسطة مرجع‬

‫تذكر أن العمليات على مرجع هي في الواقع عمليات على كائن يشري إليه املرجع ‪:‬‬

‫;‪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‬‬ ‫•‬

‫‪Using References to Avoid Copies‬‬ ‫استخدام املراجع لتجنب النسخ‬

‫قد ﻻ يكون مجدياً نسخ كائنات فئة أو حاويات كبرية الحجم‪.‬‬


‫ناهيك أنه ال يمكن نسخ بعض أنواع الفئات (بما في ذلك أنواع ‪.)IO‬‬ ‫•‬
‫لذا يجب أن تستخدم الدوال معلمات مرجع لتعمل على كائنات نوع غري قابل للنسخ‪.‬‬ ‫•‬

‫مثالً ‪ ،‬سنكتب دالة تقارن الطول لسلسلتني‪.‬‬


‫• وألن السالسل قد تكون طويلة ‪ ،‬فسنود أن نتجنب نسخها ‪ ،‬ولذلك نستخدم معلمات مراجع‪.‬‬
‫وألن املقارنة بني سلسلتني ال تتضمن تغيريهما ‪ ،‬فسنجعل تلك املعلمات مراجع لثابت ‪:‬‬ ‫•‬

‫‪// compare length of two strings‬‬


‫)‪bool isShorter(const string &s1, const string &s2‬‬
‫{‬
‫;)(‪return s1.size() < s2.size‬‬
‫}‬

‫كما سرنى في الفقرة ‪ ، 3.2.6‬ينبغي أن تستخدم الدوال مراجع لثابت ملعلمات مرجعية ال تحتاج إلى تغيريها‪.‬‬
‫أفضل املمارسات‬

‫ينبغي على معلمات مرجع ﻻ يتم تغيريها داخل دالة أن تشري لثابت‪.‬‬

‫استخدام معلمات املرجع لتعيد معلومات إضافية‬


‫‪Using Reference Parameters to Return Additional Information‬‬

‫يمكن للدالة إعادة قيمة واحدة فقط‪.‬‬


‫مع ذلك ‪ ،‬في بعض األحيان نريد من دالة ما أن تعيد أكرث من قيمة لديها‪.‬‬ ‫•‬
‫تسمح لنا معلمات املرجع بإعادة عدة نتائج بشكل فعال‪.‬‬ ‫•‬
‫كمثال ‪ ،‬سنقوم بتعريف دالة باسم ‪، find_char‬‬ ‫•‬
‫ستعيد موضع أول تكرار لحرف معني في سلسلة‪ .‬كما نود أن تعيد أيضاً عدد مرات ظهور ذلك الحرف‪.‬‬ ‫•‬
‫فكيف يمكننا تعريف دالة تعيد الفهرس وعدد مرات التكرار؟‬ ‫•‬
‫بإمكاننا تعريف نوع جديد يحتوي على الفهرس والعدد‪.‬‬ ‫•‬

‫لكن أسهل حل هو تمرير وسيطة مرجع إضافية تحتفظ بعدد مرات التكرار ‪:‬‬

‫‪// returns index of first occurrence of c in s‬‬


‫‪// the reference parameter occurs counts how often c occurs‬‬
‫‪string::size_type find_char(const string &s, char c,‬‬
‫)‪string::size_type &occurs‬‬
‫{‬
‫;)(‪auto ret = s.size‬‬ ‫‪// position of first occurrence, if any‬‬
‫;‪occurs = 0‬‬ ‫‪// set occurrence count parameter‬‬
‫{ )‪for (decltype(ret) i = 0; i != s.size(); ++i‬‬
‫{ )‪if (s[i] == c‬‬
‫))(‪if (ret == s.size‬‬
‫;‪ret = i‬‬ ‫‪// remember first occurrence of c‬‬
‫;‪++occurs‬‬ ‫‪// increment occurrence count‬‬
‫}‬
‫}‬
‫;‪return ret‬‬ ‫‪// count is returned implicitly in occurs‬‬
‫}‬

‫عندما نستدعي ‪ ، find_char‬يتعني علينا تمرير ثالث وسيطات ‪:‬‬ ‫•‬


‫سلسلة نبحث فيها ‪ ،‬وحرف يجب البحث عنه ‪ ،‬وكائن ‪ size_type‬يحتفظ بعدد التكرارات‪.‬‬ ‫•‬
‫وبافرتاض أن ‪ s‬عبارة عن سلسلة ‪ ،‬و ‪ ctr‬هو كائن من نوع ‪، size_type‬‬ ‫•‬
‫يمكننا استدعاء ‪ find_char‬على النحو التالي‪:‬‬ ‫•‬

‫;)‪auto index = find_char(s, 'o', ctr‬‬

‫بعد اﻻستدعاء ‪ ،‬ستكون قيمة ‪ ctr‬هي عدد مرات حدوث ‪، o‬‬ ‫•‬
‫وسوف تشري ‪ index‬ﻷول تكرار إن وجد‪.‬‬ ‫•‬
‫مساويا لـ ()‪ s.size‬وسيساوي ‪ ctr‬صفرًا‪.‬‬
‫ً‬ ‫عدا ذلك ‪ ،‬سيكون ‪index‬‬ ‫•‬

‫تمارين القسم ‪6.2.2‬‬


‫التمرين ‪:6.11‬‬
‫مرجعا‪.‬‬
‫ً‬ ‫اكتب واخترب إصدار ‪ reset‬خاص بك يأخذ‬
‫التمرين ‪:6.12‬‬
‫‪ -‬أعد كتابة برنامج التمرين ‪ 6.10‬فقرة ‪ 6.2.1‬باستخدام مراجع بدال ً من مؤرشات ملبادلة قيمة اثنني ‪.ints‬‬
‫‪ -‬ما هو اإلصدار الذي تعتقد أنه سيكون أسهل في االستخدام وملاذا؟‬
‫التمرين ‪:6.13‬‬
‫بافرتاض أن ‪ T‬هو اسم نوع ‪ ،‬فرس الفرق بني دالتني تم إعالنهما كالتالي ‪:‬‬
‫)‪void f(T‬‬
‫‪void f(T&).‬‬

‫التمرين ‪:6.14‬‬
‫مرجعيا‪.‬‬
‫ً‬ ‫نوعا‬
‫‪ -‬قدم مثاال ً يوضح مىت يجب أن تكون املعلمة ً‬
‫مرجعا‪.‬‬
‫ً‬ ‫‪ -‬أعط مثاال ً عندما ال يجب أن تكون املعلمة‬
‫التمرين ‪:6.15‬‬
‫مرجعا لـثابت لكن ‪ occurs‬مرجع‬
‫ً‬ ‫‪ -‬ارشح املنطق لنوع كل من معلمات ‪ find_char‬لكن خصوصاً ‪ :‬ملاذا يُعد ‪s‬‬
‫عادي؟‬
‫‪ -‬لم هاتان املعلمتان مرجعيتان ‪ ،‬لكن املعلمة ‪ char c‬ليست كذلك؟‬
‫مرجعا عادياً لغري ثابت؟‬
‫ً‬ ‫‪ -‬ما الذي سيحدث لو جعلنا ‪s‬‬
‫‪ -‬وماذا لو جعلنا ‪ occurs‬مرجعاً لثابت؟‬

‫‪6.2.3. const Parameters and Arguments‬‬ ‫‪ 6٫2٫3‬املعلمات والوسيطات الثابتة‬

‫عندما نستخدم معلمات ثابتة ‪ ،‬من املهم أن نتذكر مناقشة الثابت عالي املستوى من فقرة ‪. 3.4.2‬‬
‫كما رأينا في تلك الفقرة ‪ ،‬فالثابت عالي املستوى هو الذي ينطبق على الكائن نفسه ‪:‬‬

‫;‪const int ci = 42‬‬ ‫‪// we cannot change ci; const is top-level‬‬


‫;‪int i = ci‬‬ ‫‪// ok: when we copy ci, its top-level const is ignored‬‬
‫;‪int * const p = &i‬‬ ‫‪// const is top-level; we can't assign to p‬‬
‫;‪*p = 0‬‬ ‫‪// ok: changes through p are allowed; i is now 0‬‬

‫تماما كما هو حال أي تهيئة أخرى ‪،‬‬


‫ً‬
‫عندما نقوم بنسخ وسيطة لتهيئة معلمة ‪ ،‬يتم تجاهل الثوابت عالية املستوى‪.‬‬ ‫•‬
‫نتيجة لذلك ‪ ،‬يتم تجاهل الثابت عالي املستوى من املعلمات‪.‬‬ ‫•‬
‫يمكننا تمرير إما كائن ثابت أو كائن غري ثابت إلى معلمة تملك قيمة ثابتة عالية املستوى‪:‬‬ ‫•‬

‫} ‪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‬الثاني خطأ‪ .‬بالرغم مما يبدو عليه اﻷمر ‪ ،‬فإن قائمة معلماتها ال تختلف عن اﻷولى‪.‬‬ ‫•‬

‫معلمات مؤرش أو مرجع مع الثابت‬


‫‪Pointer or Reference Parameters and const‬‬

‫وألن املعلمات تتم تهيئتها بنفس طريقة املتغريات ‪ ،‬فقد يكون من املفيد تذكر قواعد التهيئة العامة‪.‬‬
‫يمكننا تهيئة كائن ثابت منخفض املستوى على كائن غري ثابت ولكن ليس العكس ‪،‬‬ ‫•‬
‫ويجب تهيئة مرجع عادي على كائن من نفس النوع‪.‬‬ ‫•‬

‫;‪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‬‬

‫يمكننا استدعاء النسخة املرجعية لـ ‪ reset‬فقط على كائنات ‪.int‬‬ ‫•‬


‫ال يمكننا تمرير حرفية أو تعبري يتم تقييمه كعدد صحيح أو كائن يتطلب تحوياًل أو كائنًا ثاب ًتا‪.‬‬ ‫•‬
‫وباملثل ‪ ،‬قد نمرر ‪ *int‬فقط إلى إصدار املؤرش لـ ‪.reset‬‬ ‫•‬
‫من ناحية أخرى ‪ ،‬يمكننا تمرير حرفية سلسلة كأول وسيطة لـ ‪ find_char‬؛‬ ‫•‬
‫معلمة املرجع لهذه الدالة هي مرجع لثابت ‪ ،‬ويمكننا تهيئة املراجع لثابت على حرفية‪.‬‬ ‫•‬

‫‪Use Reference to const When Possible‬‬ ‫استخدام مرجع لثابت عند اﻹمكان‬

‫من الخطأ الشائع إلى حد ما تعريف معلمات ال تغريها دالة مثل مرجع (عادي)‪.‬‬
‫انطباعا مضلاًل بأن الدالة قد تغري قيمة وسيطتها‪.‬‬
‫ً‬ ‫يؤدي القيام بذلك إلى منح مستدعي الدالة‬ ‫•‬
‫عالوة على ذلك ‪ ،‬فإن استخدام مرجع بدال ً من مرجع لثابت بال داع يحد من نوع الوسيطات اليت يمكن‬ ‫•‬
‫استخدامها مع الدالة‪.‬‬
‫كما رأينا للتو ‪ ،‬ال يمكننا تمرير كائن ثابت ‪ ،‬أو حرفي ‪ ،‬أو كائن يتطلب التحويل إلى معلمة مرجع عادية‪.‬‬ ‫•‬
‫يمكن أن يكون تأثري هذا الخطأ واسع االنتشار بشكل مدهش‪.‬‬ ‫•‬
‫على سبيل املثال ‪ ،‬فكر في دالتنا ‪ find_char‬من فقرة ‪.2.2.6‬‬ ‫•‬
‫مرجعا لثابت‪.‬‬
‫ً‬ ‫جعلت تلك الدالة (بشكل صحيح) معلمتها السلسلة‬ ‫•‬
‫عرفنا هذه املعلمة كسلسلة مرجع عادية & ‪:‬‬
‫إن ّ‬ ‫•‬

‫&‪// bad design: first parameter should be const string‬‬


‫;)‪string::size_type find_char(string &s, char c, string::size_type &occurs‬‬

‫حينها لن يمكننا استدعاء ‪ find_char‬إﻻ على كائن سلسلة‪ .‬وسيكون استدعاء مثل‬

‫;)‪find_char("Hello World", 'o', ctr‬‬

‫استدعاء فاشالً في وقت الرتجمة‪ .‬بدقة أكرث ‪،‬‬


‫تعرف (بشكل صحيح) معلماتها مراجع لثابت‪.‬‬
‫قد ال نتمكن من استخدام إصدار ‪ find_char‬هذا مع دوال ّ‬ ‫•‬
‫فمثالً ‪ ،‬قد نرغب في استخدام ‪ find_char‬داخل دالة تقرر ما إذا كانت سلسلة ما تمثل جملة ‪:‬‬ ‫•‬

‫{ )‪bool is_sentence(const string &s‬‬


‫‪// if there's single period at end of s, then s is sentence‬‬
‫;‪string::size_type ctr = 0‬‬
‫;‪return find_char(s, '.', ctr) == s.size() - 1 && ctr == 1‬‬ ‫}‬

‫إذا أخذ ‪ find_char‬سلسلة مرجع عادية & ‪ ،‬سيكون استدعاء ‪ find_char‬هذا خطأ في وقت الرتجمة‪.‬‬ ‫•‬
‫تكمن املشكلة في أن ‪ s‬مرجع لسلسلة ثابتة ‪ ،‬لكن تم تعريف ‪( find_char‬خطًأ) لتأخذ مرجعاً عادياً‪.‬‬ ‫•‬
‫قد يكون من املغري محاولة إصالح هذه املشكلة عن طريق تغيري نوع املعلمة في ‪.is_sentence‬‬ ‫•‬
‫لكن هذا اإلصالح لن يؤدي إال إلى انتشار الخطأ ‪ -‬يمكن ملستدعي ‪ is_sentence‬تمرير سالسل غري‬ ‫•‬
‫ثابتة فقط‪.‬‬

‫الطريقة الصحيحة إلصالح هذه املشكلة هي إصالح املعلمة في ‪.find_char‬‬


‫فإذا لم يكن من املمكن تغيري ‪، find_char‬‬ ‫•‬
‫فسنعرف نسخة سلسلة محلية من داخل ‪ is_sentence‬ونمرر تلك السلسلة إلى ‪.find_char‬‬
‫ّ‬ ‫•‬

‫تمارين القسم ‪6.2.3‬‬


‫تمرين ‪:6.16‬‬
‫‪ -‬الدالة التالية ‪ ،‬بالرغم من كونها رشعية ‪ ،‬إال أنها أقل فائدة مما قد تكون عليه‪.‬‬
‫‪ -‬حدد وصحح القيد على هذه الدالة‪:‬‬
‫} ;)(‪bool is_empty(string& s) { return s.empty‬‬

‫التمرين ‪: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‬‬
‫‪ -‬مىت يجب أن تكون معلمات املرجع مراجع لثابت؟‬
‫مرجعا لثابت؟‬
‫ً‬ ‫‪ -‬ماذا يحدث إذا جعلنا معلمة مرجع عادي ًة بينما يمكن أن تكون‬

‫‪6.2.4. Array Parameters‬‬ ‫‪ 6٫2٫4‬معلمات مصفوفة‬

‫تملك املصفوفات مزيتان خاصة تؤثران على كيفية تعريفنا واستخدامنا للدوال اليت تعمل عليها ‪:‬‬
‫اﻷولى أنه ال يمكننا نسخ مصفوفة‪ ،‬والثانية أنه عندما نستخدم مصفوفة سيتم تحويلها (عاد ًة) إلى مؤرش‪.‬‬ ‫•‬
‫وألننا ال نستطيع نسخ مصفوفة ‪ ،‬فال يمكننا تمرير مصفوفة بالقيمة‪.‬‬ ‫•‬
‫وألنه يتم تحويل املصفوفات إلى مؤرشات ‪ ،‬فعندما نقوم بتمرير مصفوفة إلى دالة ‪،‬‬ ‫•‬
‫فإننا في الواقع نمرر مؤرشًا ألول عنرص في املصفوفة‪.‬‬ ‫•‬

‫وبالرغم من أننا ال نستطيع تمرير مصفوفة بالقيمة ‪ ،‬إﻻ أنه بإمكاننا كتابة ُمعامل يشبه مصفوفة ‪:‬‬

‫‪// despite appearances, these three declarations of print are equivalent‬‬


‫*‪// each function has a single parameter of type const int‬‬
‫;)*‪void print(const int‬‬
‫‪void print(const int[]);// shows intent that function takes array‬‬
‫)‪void print(const int[10]); //dimension for documentation purposes (at best‬‬

‫بغض النظر عما يبدو ‪ ،‬فإن هذه اإلعالنات متكافئة ‪:‬‬ ‫•‬
‫فكل منها يعلن عن دالة بمعامل واحد من نوع مؤرش عدد صحيح ثابت‪.‬‬ ‫•‬
‫عندما يتحقق املرتجم من استدعاء ‪، print‬‬ ‫•‬
‫فإنه يفحص فقط كون الوسيطة تحمل نوع مؤرش عدد صحيح ثابت ‪:‬‬ ‫•‬

‫;}‪int i = 0, j[2] = {0, 1‬‬


‫;)‪print(&i‬‬ ‫*‪// ok: &i is int‬‬
‫;)‪print(j‬‬ ‫]‪// ok: j is converted to int* that points to j[0‬‬

‫تلقائيا إلى مؤرش ألول عنرص في املصفوفة ؛‬


‫ً‬ ‫إذا مررنا مصفوفة إلى ‪ ، print‬يتم تحويل هذه الوسيطة‬ ‫•‬
‫أما حجم املصفوفة فموضوع غري ذي صلة‪.‬‬ ‫•‬

‫تحذير‬

‫مثل أي كود يستخدم مصفوفات ‪ ،‬يجب أن تضمن الدوال اليت تأخذ معلمات مصفوفة‬
‫أن تبقى جميع استخدامات املصفوفة ضمن حدودها‪.‬‬

‫وألن املصفوفات يتم تمريرها كمؤرشات ‪ ،‬فإن الدوال عادة ال تعرف حجم املصفوفة املعطاة لها‪.‬‬ ‫•‬
‫يجب أن تعتمد على املعلومات اإلضافية املقدمة من قبل املستدعي‪.‬‬ ‫•‬
‫هناك ثالث تقنيات شائعة تستخدم إلدارة معلمات املؤرش‪.‬‬ ‫•‬

‫استخدام عالمة لتحدد مدى املصفوفة‬


‫‪Using a Marker to Specify the Extent of an Array‬‬

‫تتطلب الطريقة األولى إلدارة وسيطات مصفوفة أن تحتوي املصفوفة نفسها على عالمة نهاية‪.‬‬
‫سالسل األحرف من نمط اليس هي مثال على هذا النهج‪.‬‬ ‫•‬
‫يتم تخزين سالسل نمط اليس في مصفوفات أحرف حيث يتبع آخر حرف من السلسلة حرف خال‪.‬‬ ‫•‬
‫تتوقف دوال تتعامل مع سالسل نمط اليس عن معالجة مصفوفة عندما ترى حر ًفا خالياً ‪:‬‬ ‫•‬

‫)‪void print(const char *cp‬‬


‫{‬
‫)‪if (cp‬‬ ‫‪// if cp is not null pointer‬‬
‫‪while (*cp) // so long as character it points to not null character‬‬
‫‪cout << *cp++; // print character and advance pointer‬‬
‫}‬
‫تعمل هذه الطريقة جي ًد ا مع بيانات توجد فيها قيمة واضحة لعالمة النهاية (كالحرف الخالي) واليت ال تظهر‬ ‫•‬
‫في البيانات العادية‪.‬‬
‫لكن سيعمل بشكل أقل جودة مع بيانات كاﻷعداد الصحيحة حيث تكون كل قيمة في النطاق قيمة مرشوعة‪.‬‬ ‫•‬

‫استخدام منهجيات املكتبة القياسية‬


‫‪Using the Standard Library Conventions‬‬

‫الطريقة الثانية املستخدمة إلدارة وسيطات مصفوفة هي تمرير مؤرشات ﻷول عنرص وآخر عنرص في املصفوفة‪.‬‬
‫هذا النهج مستوحى من التقنيات املستخدمة في املكتبة القياسية‪.‬‬ ‫•‬
‫سنتعرف على املزيد حول هذا النمط من الربمجة في الجزء الثاني‪.‬‬ ‫•‬
‫باستخدام هذا األسلوب ‪ ،‬سنطبع عنارص في مصفوفة على النحو التالي‪:‬‬ ‫•‬

‫)‪void print(const int *beg, const int *end‬‬


‫{‬
‫‪// print every element starting at beg up to but not including end‬‬
‫)‪while (beg != end‬‬
‫‪cout << *beg++ << endl; // print current element & advance pointer‬‬
‫}‬

‫تستخدم ‪ while‬عامل إلغاء املرجع و تزايد الالحقة لطباعة العنرص الحالي وتتقدم عنرص واحد في كل مرة‬ ‫•‬
‫عرب املصفوفة‪.‬‬
‫تتوقف الحلقة عندما تساوي ‪ beg‬النهاية ‪.end‬‬ ‫•‬
‫والستدعاء هذه الدالة ‪،‬‬ ‫•‬
‫نقوم بتمرير مؤرشين ‪ -‬أحدهما ألول عنرص نريد طباعته واآلخر ملا بعد آخر عنرص ‪:‬‬ ‫•‬

‫;}‪int j[2] = {0, 1‬‬


‫‪// j is converted to pointer to first element in j‬‬
‫‪// second argument is pointer to one past end of j‬‬
‫‪print(begin(j), end(j)); // begin and end functions‬‬

‫تعد هذه الدالة آمنة ‪ ،‬طاملا أن املستدعي يحسب املؤرشات بشكل صحيح‪.‬‬ ‫•‬
‫هنا ندع مكتبة دوال بداية ونهاية توفر هذه املؤرشات‪.‬‬ ‫•‬

‫‪Explicitly Passing a Size Parameter‬‬ ‫تمرير معلمة حجم بشكل رصيح‬

‫الطريقة الثالثة لوسيطات مصفوفة ‪ -‬وهي الشائعة في برامج يس وبرامج يس‪ ++‬األقدم – تتمثل في تحديد معلمة‬
‫ثانية تشري إلى حجم املصفوفة‪.‬‬
‫باستخدام هذا األسلوب ‪ ،‬سنعيد كتابة ‪ print‬على النحو التالي‪:‬‬

‫‪// const int ia[] is equivalent to const int* ia‬‬


‫‪// size is passed explicitly and used to control access to elements of ia‬‬
‫)‪void print(const int ia[], size_t size‬‬
‫{‬
‫)‪for (size_t i = 0; i != size; ++i‬‬
‫;‪cout << ia[i] << endl‬‬
‫}‬

‫يستخدم هذا اإلصدار معلمة ‪ size‬لتحديد عدد العنارص املطلوب طباعتها‪.‬‬ ‫•‬
‫عندما نستدعي ‪ ، print‬يجب أن نمرر هذه املعلمة اإلضافية‪:‬‬ ‫•‬

‫;} ‪int j[] = { 0, 1‬‬ ‫‪// int array of size 2‬‬


‫;))‪print(j, end(j) - begin(j‬‬

‫يتم تنفيذ الدالة بأمان طاملا أن الحجم الذي تم تمريره ال يزيد عن حجم املصفوفة الفعلي ‪.‬‬

‫‪Array Parameters and const‬‬ ‫معلمات املصفوفة والثابت‬

‫الحظ أن جميع اإلصدارات الثالثة من دالتنا ‪ print‬حددت معلمات مصفوفتها كمؤرشات لثابت‪.‬‬
‫املناقشة الواردة في الفقرة ‪ 3.2.6‬تنطبق بالتساوي على املؤرشات مثلها مثل املراجع‪.‬‬ ‫•‬
‫عندما ال تحتاج الدالة إلى الوصول للكتابة على عنارص مصفوفة ‪،‬‬ ‫•‬
‫حينها يجب أن تكون معلمة املصفوفة مؤرشًا لثابت‪.‬‬ ‫•‬
‫عاديا لنوع غري ثابت ما لم تكن بحاجة إلى تغيري قيمة العنرص‪.‬‬
‫ً‬ ‫يجب أن تكون املعلمة مؤرشًا‬ ‫•‬

‫‪Array Reference Parameters‬‬ ‫معلمات مرجع مصفوفة‬

‫كما يمكننا تعريف متغري يمثل مرجعاً ملصفوفة ‪ ،‬يمكننا تعريف معلمة تكون مرجعاً ملصفوفة‪.‬‬
‫وكالعادة ‪ ،‬ستكون معلمة املرجع مربوطة بالوسيطة املقابلة ‪ ،‬واليت ستكون في هذه الحالة عبارة عن مصفوفة ‪:‬‬

‫‪// ok: parameter is reference to array; dimension is part of type‬‬


‫)]‪void print(int (&arr)[10‬‬
‫{‬
‫)‪for (auto elem : arr‬‬
‫;‪cout << elem << endl‬‬
‫}‬

‫ملحوظة‬

‫تعترب األقواس حول املتغري ‪ &arr‬رضورية ؛‬


‫فبدونها سيبدو املتغري كمصفوفة مراجع ‪ ،‬أما مع اﻷقواس فاملتغري مرجع ملصفوفة من أعداد صحيحة ‪:‬‬
‫)]‪f(int &arr[10‬‬ ‫‪// error: declares arr as array of references‬‬
‫‪f(int (&arr)[10]) // ok: arr is reference to array of ten ints‬‬

‫ونظر ًا ألن حجم املصفوفة جزء من نوعها ‪ ،‬فمن اآلمن االعتماد على الحجم في بنية الدالة‪.‬‬ ‫•‬
‫مع ذلك ‪ ،‬فإن حقيقة أن الحجم جزء من النوع يحد من فائدة هذا اإلصدار من ‪.print‬‬ ‫•‬
‫حيث لن يمكننا استدعاء هذه الدالة إﻻ ملصفوفة من عرشة أعداد صحيحة بالضبط‪:‬‬ ‫•‬

‫;}‪int i = 0, j[2] = {0, 1‬‬


‫;}‪int k[10] = {0,1,2,3,4,5,6,7,8,9‬‬
‫;)‪print(&i‬‬ ‫‪// error: argument is not array of ten ints‬‬
‫;)‪print(j‬‬ ‫‪// error: argument is not array of ten ints‬‬
‫;)‪print(k‬‬ ‫‪// ok: argument is array of ten ints‬‬

‫سرنى في الفقرة ‪ 16.1.1‬كيف يمكننا كتابة هذه الدالة بطريقة تسمح لنا بتمرير معلمة مرجع ملصفوفة من أي‬
‫حجم‪.‬‬

‫‪Passing a Multidimensional Array‬‬ ‫تمرير مصفوفة متعددة اﻷبعاد‬

‫تذكر أنه ال توجد مصفوفات متعددة األبعاد في لغة يس‪ .++‬بدال ً من ذلك ‪ ،‬فما تبدو كمصفوفة متعددة األبعاد ما‬
‫هي إﻻ مصفوفة من مصفوفات أخرى‪.‬‬
‫وكما هو حال أي مصفوفة ‪ ،‬يتم تمرير املصفوفة متعددة األبعاد كمؤرش ألول عنرص فيها‪.‬‬ ‫•‬
‫ونظرًا ألننا نتعامل مع مصفوفة من مصفوفات ‪ ،‬فإن ذلك العنرص سيكون مصفوفة ‪،‬‬ ‫•‬
‫وبالتالي فإن املؤرش هو مؤرش ملصفوفة‪.‬‬ ‫•‬
‫يعد حجم البُعد الثاني (وأي بُعد الحق) هو جزء من نوع العنرص ويجب تحديده‪:‬‬ ‫•‬

‫‪//matrix points 1st element in array whose elements are arrays of ten ints‬‬
‫} ‪void print(int (*matrix)[10], int rowSize) { /* ... */‬‬

‫يعلن ‪ matrix‬كمؤرش ملصفوفة من عرشة أعداد صحيحة‪.‬‬

‫ملحوظة‬

‫مرة أخرى ‪ ،‬األقواس حول املتغري ‪ *matrix‬رضورية ؛‬


‫فبدونها سيبدو املتغري كمصفوفة مؤرشات ‪ ،‬أما مع اﻷقواس فاملتغري مرجع ملؤرشات ﻷعداد صحيحة ‪:‬‬
‫;]‪int *matrix[10‬‬ ‫‪// array of ten pointers‬‬
‫;]‪int (*matrix)[10‬‬ ‫‪// pointer to array of ten ints‬‬

‫أيضا تعريف دالتنا باستخدام بنية املصفوفة‪.‬‬


‫يمكننا ً‬
‫وكالعادة سيتجاهل املرتجم أول بعد ‪ ،‬لذا فمن األفضل عدم تضمينه‪:‬‬

‫‪// equivalent definition‬‬


‫} ‪void print(int matrix[][10], int rowSize) { /* . . . */‬‬

‫يعلن عن ‪ matrix‬ليشبه مصفوفة ثنائية األبعاد‪.‬‬ ‫•‬


‫لكن في الواقع ‪ ،‬املعلمة ما هي إﻻ مؤرش ملصفوفة من ‪ 10‬أعداد صحيحة ‪.ints‬‬ ‫•‬
‫تمارين القسم ‪6.2.4‬‬

‫التمرين ‪: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‬‬
‫}‬

‫‪ 6٫2٫5‬دالة ‪ : main‬معالجة خيارات سطر اﻷوامر‬


‫‪6.2.5. main: Handling Command-Line Options‬‬

‫يبدو أن الدالة ‪ main‬تعترب مثاال ً جيداً على كيفية تمرير برامج يس‪ ++‬مصفوفات إلى الدوال‪.‬‬
‫نعرف ‪ main‬بقائمة معلمات فارغة ‪:‬‬
‫فحىت اآلن ‪ ،‬كنا ّ‬

‫} ‪int main() { ...‬‬

‫مع ذلك ‪ ،‬نحتاج أحيا ًنا إلى تمرير وسيطات إلى ‪.main‬‬ ‫•‬
‫شيوعا لوسيطات ‪ main‬هو السماح للمستخدم بتحديد مجموعة خيارات لتوجيه تشغيل‬
‫ً‬ ‫االستخدام األكرث‬ ‫•‬
‫الربنامج‪.‬‬
‫فعلى سبيل املثال ‪ ،‬نفرتض أن برنامجنا ‪ main‬موجود في ملف قابل للتنفيذ يسمى ‪، prog‬‬ ‫•‬
‫فقد نقوم بتمرير خيارات إلى الربنامج على النحو التالي ‪:‬‬ ‫•‬

‫‪prog -d -o ofile data0‬‬

‫يتم تمرير خيارات سطر األوامر هذه إلى ‪ main‬في معلمتني (اختياريتني)‪:‬‬

‫} ‪int main(int argc, char *argv[]) { ...‬‬

‫املعلمة الثانية ‪ ، argv ،‬عبارة عن مصفوفة مؤرشات لسالسل أحرف على نمط ‪.C‬‬ ‫•‬
‫املعلمة األولى ‪ ، argc ،‬تمرر عدد السالسل في تلك املصفوفة‪.‬‬ ‫•‬
‫ونظرًا ألن املعلمة الثانية عبارة عن مصفوفة ‪ ،‬فيمكننا بدال ً من ذلك تعريف ‪ main‬كـ ‪:‬‬ ‫•‬
‫} ‪int main(int argc, char **argv) { ...‬‬

‫ليشري إلى أن ‪ argv‬تؤرش لحرف *‪.‬‬


‫عندما يتم تمرير وسيطات إلى ‪، main‬‬
‫فإن أول عنرص في ‪ argv‬يشري إما إلى اسم الربنامج أو إلى السلسلة الخالية‪.‬‬ ‫•‬
‫تمرر العنارص الالحقة الوسيطات املتوفرة في سطر األوامر‪.‬‬ ‫•‬
‫يتم ضمان أن يكون العنرص ما بعد املؤرش األخري صفر ‪.‬‬ ‫•‬

‫بالنظر إلى سطر األوامر السابق ‪ ،‬سيكون ‪ ، argc 5‬وستحتفظ ‪ argv‬بسالسل أحرف نمط اليس التالية‪:‬‬

‫;"‪argv[0] = "prog‬‬ ‫‪// or argv[0] might point to an empty string‬‬


‫;"‪argv[1] = "-d‬‬
‫;"‪argv[2] = "-o‬‬
‫;"‪argv[3] = "ofile‬‬
‫;"‪argv[4] = "data0‬‬
‫;‪argv[5] = 0‬‬

‫تحذير‬

‫عند استخدام وسيطات في ‪ ، argv‬تذكر أن الوسيطات االختيارية تبدأ من ]‪ argv[1‬؛‬


‫ذلك ﻷن ]‪ argv[0‬يحمل اسم الربنامج ‪ ،‬وليس إدخال املستخدم‪.‬‬

‫تمارين القسم ‪6.2.5‬‬

‫تمرين ‪:6.25‬‬
‫اكتب دالة رئيسية تأخذ وسيطتني‪ .‬اربط الوسيطات املتوفرة واطبع السلسلة الناتجة‪.‬‬

‫التمرين ‪:6.26‬‬
‫برنامجا يقبل الخيارات املعروضة في هذا القسم‪.‬‬
‫ً‬ ‫‪ -‬اكتب‬
‫‪ -‬اطبع قيم الوسيطات اليت تم تمريرها إلى ‪.main‬‬

‫‪6.2.6. Functions with Varying Parameters‬‬ ‫‪ 6٫2٫2‬دوال ذات معلمات مختلفة‬

‫في بعض األحيان ال نعرف مسب ًقا عدد الوسيطات اليت نحتاج تمريرها لدالة‪.‬‬
‫فعلى سبيل املثال ‪ ،‬قد نرغب في كتابة روتني لطباعة رسائل الخطأ اليت تم إنشاؤها من برنامجنا‪.‬‬ ‫•‬
‫نود استخدام دالة واحدة لطباعة رسائل الخطأ هذه من أجل التعامل معها بطريقة موحدة‪ .‬مع ذلك ‪،‬‬ ‫•‬
‫قد تمرر استدعاءات مختلفة لدالة طباعة األخطاء وسيطات مختلفة تتوافق مع أنواع مختلفة من رسائل‬ ‫•‬
‫الخطأ‪.‬‬
‫يوفر املعيار الجديد طريقتني أساسيتني لكتابة دالة تأخذ عد ًدا متفاو ًتا من الوسيطات ‪:‬‬
‫فإذا كانت جميع الوسيطات تملك نفس النوع ‪ ،‬فيمكننا تمرير نوع مكتبة باسم قائمة_مهيـئ ‪.‬‬ ‫•‬
‫أما إذا اختلفت أنواع الوسيطات ‪ ،‬فيمكننا كتابة نوع خاص من الدوال ‪،‬‬ ‫•‬
‫تعرف باسم القالب املتغري ‪ ،variadic template‬وسنغطيه في الفصل‪. ١٦‬‬ ‫•‬

‫أيضا على نوع معلمة خاص ‪ ،‬عالمة حذف ‪،ellipsis‬‬


‫يحتوي يس‪ً ++‬‬
‫يمكن استخدامه لتمرير عدد متنوع من الوسيطات‪.‬‬ ‫•‬
‫سنلقي نظرة بإيجاز على معلمات عالمة الحذف في هذا القسم‪ .‬ومع ذلك ‪،‬‬ ‫•‬
‫تجدر اإلشارة إلى أنه يجب استخدام هذا القسم عاد ًة فقط في الربامج اليت تحتاج إلى واجهة بدوال لغة ‪.C‬‬ ‫•‬

‫‪initializer_list Parameters‬‬ ‫معلمات قائمة املهئي‬

‫يمكننا كتابة دالة تأخذ عد ًد ا غري معروف من الوسيطات من نوع واحد باستخدام معلمة قائمة_املهئي ‪.‬‬
‫قائمة املهئي هي نوع مكتبة يمثل مصفوفة قيم من نوع محدد‪.‬‬ ‫•‬
‫يتم تعريف هذا النوع في العنوان ‪.initializer_list‬‬ ‫•‬
‫يتم رسد العمليات اليت توفرها القائمة في الجدول ‪.6.1‬‬ ‫•‬

‫الجدول ‪ .6.1‬العمليات على قائمة املهيئات‬

‫‪Default initialization; an empty list of‬‬


‫;‪initializer_list<T> lst‬‬
‫‪elements of type T.‬‬

‫‪lst has as many elements as there are‬‬


‫>‪initializer_list<T‬‬ ‫‪initializers; elements are copies of the‬‬
‫;}…‪lst{a,b,c‬‬ ‫‪corresponding initializers.‬‬
‫‪Elements in the list are const.‬‬

‫‪Copying or assigning an initializer_list does‬‬


‫)‪lst2(lst‬‬ ‫‪not copy the elements in the list.‬‬
‫‪lst2 = lst‬‬ ‫‪After the copy, the original and the copy‬‬
‫‪share the elements.‬‬

‫)(‪lst.size‬‬ ‫‪Number of elements in the list.‬‬

‫)(‪lst.begin‬‬ ‫‪Return a pointer to the first and one past‬‬


‫)(‪lst.end‬‬ ‫‪the last element in lst.‬‬

‫مثل املتجه ‪ ،‬فإن قائمة_املهئي عبارة عن نوع قالب‪.‬‬ ‫•‬


‫نعرف قائمة املهئي ‪ ،‬يجب أن نحدد نوع العنارص اليت ستحتويها القائمة ‪:‬‬
‫عندما ّ‬ ‫•‬

‫;‪initializer_list<string> ls‬‬ ‫‪// initializer_list of strings‬‬


‫;‪initializer_list<int> li‬‬ ‫‪// initializer_list of ints‬‬

‫لكن على عكس املتجه ‪،‬‬


‫دائما قيما ثابتة ؛‬
‫ً‬ ‫فإن عنارص قائمة املهئي تكون‬ ‫•‬
‫وال توجد طريقة لتغيري قيمة عنرص فيها‪.‬‬ ‫•‬
‫يمكننا كتابة دالتنا إلنتاج رسائل خطأ من عدد متنوع من الوسيطات على النحو التالي‪:‬‬

‫)‪void error_msg(initializer_list<string> il‬‬


‫{‬
‫)‪for (auto beg = il.begin(); beg != il.end(); ++beg‬‬
‫; " " << ‪cout << *beg‬‬
‫;‪cout << endl‬‬
‫}‬

‫عمليات ‪ begin and end‬على كائنات قائمة املهئي تشبه أعضاء متجه ‪ begin and end‬املقابلة‪.‬‬ ‫•‬
‫يعطينا العضو ()‪ begin‬مؤرشًا ألول عنرص في القائمة ‪،‬‬ ‫•‬
‫أما ()‪ end‬فهي مؤرش ملا بعد آخر عنرص‪.‬‬ ‫•‬
‫تهئي دالتنا ‪ beg‬لتشري ﻷول عنرص وتتكرر خالل كل عنرص في قائمة املهئي‪.‬‬ ‫•‬
‫في بنية الحلقة ‪ ،‬قمنا بإلغاء مرجع ‪ beg‬من أجل الوصول إلى العنرص الحالي وطباعة قيمته‪.‬‬ ‫•‬

‫عندما نمرر سلسلة قيم إلى معلمة ‪ ، initializer_list‬يجب أن نحرص التسلسل بأقواس متعرجة‪:‬‬

‫‪// expected, actual strings‬‬


‫)‪if (expected != actual‬‬
‫;)}‪error_msg({"functionX", expected, actual‬‬
‫‪else‬‬
‫;)}"‪error_msg({"functionX", "okay‬‬

‫هنا نستدعي نفس الدالة ‪ ، error_msg ،‬ونمرر ثالث قيم في أول استدعاء وقيمتني في اﻻستدعاء الثاني‪.‬‬ ‫•‬
‫أيضا‪.‬‬
‫يمكن لدالة تحتوي على معلمة قائمة_مهئي أن تحتوي على معلمات أخرى ً‬ ‫•‬
‫فعلى سبيل املثال ‪ ،‬قد يحتوي نظامنا للتصحيح على فئة تسمى ‪، ErrCode‬‬ ‫•‬
‫أنواعا مختلفة من األخطاء‪.‬‬
‫ً‬ ‫وهي فئة تمثل‬ ‫•‬

‫يمكننا مراجعة برنامجنا ألخذ ‪ ErrCode‬إضافة إلى قائمة املهئي على النحو التالي ‪:‬‬

‫)‪void error_msg(ErrCode e, initializer_list<string> il‬‬


‫{‬
‫;" ‪cout << e.msg() << ":‬‬
‫)‪for (const auto &elem : il‬‬
‫; " " << ‪cout << elem‬‬
‫;‪cout << endl‬‬
‫}‬

‫ونظرًا ألن قائمة املهئي تملك أعضاء ‪ ، begin and end‬فيمكننا استخدام ‪ for‬النطاق ملعالجة العنارص‪.‬‬ ‫•‬
‫هذا الربنامج ‪ ،‬مثل نسختنا السابقة ‪ ،‬يتكرر عنرصًا في كل مرة خالل قائمة القيم اليت تم تمريرها إلى‬ ‫•‬
‫املعلمة ‪.il‬‬
‫الستدعاء هذا اإلصدار ‪ ،‬نحتاج إلى مراجعة استدعاءاتنا لتمرير وسيطة ‪: ErrCode‬‬
‫)‪if (expected != actual‬‬
‫;)}‪error_msg(ErrCode(42), {"functionX", expected, actual‬‬
‫‪else‬‬
‫;)}"‪error_msg(ErrCode(0), {"functionX", "okay‬‬

‫‪Ellipsis Parameters‬‬ ‫معلمات عالمة الحذف‬

‫ُأوجدت معلمات ‪ Ellipsis‬في يس‪ ++‬للسماح للربامج بالتفاعل مع كود ‪ C‬يستخدم قسم مكتبة ‪ C‬يسمى‬
‫‪.varargs‬‬
‫وعموماً ‪ ،‬ال ينبغي استخدام معلمة عالمة الحذف ألغراض أخرى‪.‬‬ ‫•‬
‫سوف تصف وثائق مرتجم يس كيفية استخدام ‪.varargs‬‬ ‫•‬

‫تحذير‬

‫يجب استخدام معلمات ‪ Ellipsis‬فقط لألنواع املشرتكة في كل من يس و يس‪ .++‬باﻷخص ‪،‬‬


‫ال يتم نسخ كائنات معظم أنواع الفئات بشكل صحيح عند تمريرها إلى معلمة عالمة الحذف‪.‬‬

‫أيا من النموذجني التاليني‪:‬‬


‫قد تظهر معلمة عالمة الحذف فقط كآخر عنرص في قائمة املعلمات ويمكن أن تتخذ ً‬

‫;)‪void foo(parm_list, ...‬‬


‫;)‪void foo(...‬‬

‫يحدد أول نموذج النوع (أو األنواع) لبعض معلمات ‪.foo‬‬ ‫•‬
‫يتم فحص الوسيطات اليت تتوافق مع املعلمات املحددة كاملعتاد‪.‬‬ ‫•‬
‫ﻻ يتم إجراء تدقيق نوع للوسيطات اليت تتوافق مع معلمة عالمة الحذف‪.‬‬ ‫•‬
‫في هذا النموذج األول ‪ ،‬تكون الفاصلة اليت تلي إعالنات املعلمات اختيارية‪.‬‬ ‫•‬

‫قسم التدريبات ‪6.2.6‬‬

‫التمرين ‪: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‬‬

‫‪6.3.1. Functions with No Return Value‬‬ ‫‪ 6٫3٫1‬دوال ال تعيد قيمة‬

‫يمكن استخدام اإلعادة بدون قيمة فقط في دالة ذات نوع إعادة ‪.void‬‬
‫الدوال اليت تعيد ‪ void‬ليس مطلوبا أن تحتوي على الجملة ٍ‬
‫أعد ‪.return‬‬ ‫•‬
‫ففي دالة ‪ ، void‬تحدث اإلعادة ضمنيا بعد آخر جملة فيها‪.‬‬ ‫•‬
‫وعاد ًة تستخدم دوال ‪ void‬جملة ِ‬
‫أعد ‪ return‬للخروج من الدالة عند نقطة متوسطة‪.‬‬ ‫•‬
‫يشبه استخدام ‪ return‬هناك استخدام جملة اكرِس ‪ break‬للخروج من حلقة‪.‬‬ ‫•‬
‫فعلى سبيل املثال ‪ ،‬يمكننا كتابة دالة مبادلة لن تعمل إن كانت القيم متطابقة ‪:‬‬ ‫•‬

‫)‪void swap(int &v1, int &v2‬‬


‫{‬
‫‪// if values are already same, no need to swap, just return‬‬
‫)‪if (v1 == v2‬‬
‫;‪return‬‬
‫‪// if we're here, there's work to do‬‬
‫;‪int tmp = v2; v2 = v1; v1 = tmp‬‬
‫‪// no explicit return necessary‬‬
‫}‬

‫تتحقق هذه الدالة أوال ً ما إذا كانت القيم متساوية ‪ ،‬فإن كان األمر كذلك ‪ ،‬تخرج من الدالة‪.‬‬ ‫•‬
‫أما إذا كانت القيم غري متساوية ‪ ،‬تقوم الدالة بمبادلتها‪.‬‬ ‫•‬
‫تحدث اإلعادة ضمنياً بعد آخر جملة تعيني‪.‬‬ ‫•‬
‫قد ﻻ تستخدم دالة ذات نوع إعادة فارغة الشكل الثاني من الجملة ِ‬
‫أعد إﻻ إلعادة نتيجة استدعاء دالة أخرى‬ ‫•‬
‫فارغا‪.‬‬
‫ً‬ ‫تعيد‬
‫يعد إعادة أي تعبري آخر من دالة فارغة خطأ وقت الرتجمة‪.‬‬ ‫•‬

‫‪6.3.2. Functions That Return a Value‬‬ ‫‪ 6٫3٫2‬دوال تعيد قيمة‬

‫يوفر الشكل الثاني من الجملة ِ‬


‫أعد نتيجة الدالة‪.‬‬
‫لكن بخالف دوال الفراغ اليت ﻻ تعيد قيمة يجب أن تعيد كل دالة إعادة قيمة‪.‬‬ ‫•‬
‫ضمنيا إلى ذلك النوع‪.‬‬
‫ً‬ ‫ويجب أن تكون للقيمة املعادة نفس نوع إعادة الدالة ‪ ،‬أو نوع يمكن تحويله‬ ‫•‬
‫بالرغم من أن يس‪ ++‬ال يمكن أن تضمن صحة النتيجة ‪،‬‬ ‫•‬
‫إال أنها تضمن أن كل عائد يتضمن نتيجة من النوع املناسب‪.‬‬ ‫•‬
‫بالرغم من أنه ال يمكنه القيام بذلك في جميع الحاالت ‪ ،‬إال أن املرتجم يحاول التأكد من إنهاء الدوال اليت‬ ‫•‬
‫تعيد قيمة فقط من خالل جملة إعادة صالحة‪.‬‬
‫فمثال‪:‬‬ ‫•‬

‫‪// incorrect return values, this code will not compile‬‬


‫)‪bool str_subrange(const string &str1, const string &str2‬‬
‫{‬
‫‪// same sizes: return normal equality test‬‬
‫))(‪if (str1.size() == str2.size‬‬
‫;‪return str1 == str2‬‬ ‫‪// ok: == returns bool‬‬
‫‪// find size of smaller string; conditional operator‬‬
‫;)(‪auto size = (str1.size() < str2.size()) ? str1.size() : str2.size‬‬
‫‪// look at each element up to size of smaller string‬‬
‫{ )‪for (decltype(size) i = 0; i != size; ++i‬‬
‫)]‪if (str1[i] != str2[i‬‬
‫;‪return ; // error #1: no return value‬‬
‫‪// compiler should detect this error‬‬
‫}‬
‫‪// error #2: control might flow off end of function without return‬‬
‫‪// compiler might not detect this error‬‬
‫}‬

‫تعترب الجملة ِ‬
‫أعد من داخل حلقة ‪ for‬خطًأ ألنه يفشل في إعادة قيمة‪.‬‬ ‫•‬
‫ينبغي أن يكتشف املرتجم هذا الخطأ‪.‬‬ ‫•‬
‫يحدث الخطأ الثاني بسبب فشل الدالة في توفري الجملة ِ‬
‫أعد بعد الحلقة‪.‬‬ ‫•‬
‫إذا استدعينا هذه الدالة بسلسلة واحدة تمثل مجموعة فرعية من السلسلة األخرى ‪،‬‬ ‫•‬
‫فسيخرج التنفيذ من سلسلة ‪.for‬‬ ‫•‬
‫يجب أن يكون هناك جملة ِ‬
‫أعد للتعامل مع تلك الحالة‪.‬‬ ‫•‬
‫قد يكتشف املرتجم هذا الخطأ وقد ال يكتشفه‪.‬‬ ‫•‬
‫معرف‪.‬‬
‫إذا لم يكتشف الخطأ ‪ ،‬فإن ما يحدث في وقت التشغيل سلوك غري ّ‬ ‫•‬

‫تحذير‬

‫أعد بعد حلقة تحتوي على ‪ return‬خطأ‪.‬‬‫يعد الفشل في توفري الجملة ِ‬


‫مع ذلك ‪ ،‬فلن تكتشف عدة مرتجمات مثل تلك األخطاء‪.‬‬

‫‪How Values Are Returned‬‬ ‫كيف يتم إعادة القيم‬


‫تماما بنفس الطريقة اليت تتم بها تهيئة املتغريات واملعلمات ‪:‬‬
‫ً‬ ‫يتم إعادة القيم‬
‫تُستخدم القيمة املعادة لتهيئة مؤقتة في موقع االستدعاء ‪ ،‬وهذا املؤقت هو نتيجة استدعاء الدالة‪.‬‬ ‫•‬
‫من املهم أن تفكر في قواعد التهيئة في الدوال اليت تعيد املتغريات املحلية‪ .‬على سبيل املثال ‪،‬‬ ‫•‬
‫قد نكتب دالة بإعطائها عداداً وكلمة ونهاية تعيد لنا صيغة الجمع للكلمة إذا كان العداد أكرب من ‪:1‬‬ ‫•‬

‫‪// return plural version of word if ctr is greater than 1‬‬


‫)‪string make_plural(size_t ctr, const string &word, const string &ending‬‬
‫{‬
‫;‪return (ctr > 1) ? word + ending : word‬‬
‫}‬

‫نوع اإلعادة لهذه الدالة هو سلسلة ‪ ،‬ما يعين أن القيمة املعادة يتم نسخها إلى موقع االستدعاء‪.‬‬ ‫•‬
‫تقوم هذه الدالة بإعادة نسخة من الكلمة ‪ ،‬أو تقوم بإعادة سلسلة مؤقتة غري مسماة ناتجة عن جمع كلمة‬ ‫•‬
‫مع نهاية‪.‬‬
‫وكما هو حال أي مرجع آخر ‪ ،‬عندما تقوم دالة بإعادة مرجع ‪،‬‬ ‫•‬
‫يكون هذا املرجع مجرد اسم آخر للكائن الذي تشري إليه‪.‬‬ ‫•‬
‫مرجعا إلى أقرص_سلسلة من معلميت سلسلة خاصة بها ‪:‬‬
‫ً‬ ‫كمثال ‪ ،‬فكر في دالة تعيد‬ ‫•‬

‫‪// return reference to shorter of two strings‬‬


‫)‪const string &shorterString(const string &s1, const string &s2‬‬
‫{‬
‫;‪return s1.size() <= s2.size() ? s1 : s2‬‬
‫}‬

‫املعلمات ونوع اإلعادة هي مراجع لسلسلة ثابتة‪.‬‬ ‫•‬


‫لن يتم نسخ السالسل عند استدعاء الدالة وﻻ عند إعادة النتيجة‪.‬‬ ‫•‬

‫أبداً ﻻ تقم بإعادة مرجع أو مؤرش إلى كائن محلي‬


‫‪Never Return a Reference or Pointer to a Local Object‬‬

‫عندما تكتمل دالة ‪ ،‬يتم تحرير مخزنها‪ .‬وبعد إنهاءها ‪ ،‬ستشري املراجع لكائنات محلية إلى ذاكرة لم تعد صالحة ‪:‬‬

‫‪// disaster: this function returns a reference to a local object‬‬


‫)(‪const string &manip‬‬
‫{‬
‫;‪string ret‬‬
‫‪// transform ret in some way‬‬
‫))(‪if (!ret.empty‬‬
‫;‪return ret‬‬ ‫!‪// WRONG: returning reference to local object‬‬
‫‪else‬‬
‫‪return "Empty"; // WRONG: "Empty" is local temporary string‬‬
‫}‬
‫أعد هذه تعيد قيمة غري محددة ‪ -‬فما سيحدث إن حاولنا استخدام القيمة املعادة من ‪ manip‬سلوك‬‫كل جمل ِ‬ ‫•‬
‫معرف‪.‬‬
‫غري ّ‬
‫مرجعا إلى كائن محلي‪.‬‬
‫ً‬ ‫واضحا أن الدالة تعيد‬
‫ً‬ ‫في اإلعادة األولى ‪ ،‬يجب أن يكون‬ ‫•‬
‫في الحالة الثانية ‪ ،‬يتم تحويل سلسلة حرفية إلى كائن سلسلة مؤقت محلي‪.‬‬ ‫•‬
‫هذا الكائن ‪ ،‬مثل السلسلة املسماة ‪ ، s‬محلي للتعامل معه‪.‬‬ ‫•‬
‫يتم تحرير املخزن الذي يوجد به املؤقت عند انتهاء الدالة‪.‬‬ ‫•‬
‫جملتا ِ‬
‫أعد كالهما تشريان إلى ذاكرة لم تعد متوفرة‪.‬‬ ‫•‬

‫نصيحة‬

‫إحدى الطرق الجيدة للتأكد من أمان إعادة ما يكون بطرح السؤال ‪ :‬إلى أي كائن موجود مسب ًقا سيشري املرجع؟‬

‫أيضا إعادة مؤرش لكائن محلي‪.‬‬


‫لنفس األسباب اليت تجعل من الخطأ إعادة مرجع لكائن محلي ‪ ،‬فمن الخطأ ً‬ ‫•‬
‫فبمجرد اكتمال الدالة ‪ ،‬سيتم تحرير الكائنات املحلية‪ .‬وسيشري املؤرش لكائن غري موجود‪.‬‬ ‫•‬

‫الدوال اليت تعيد أنواع فئة وعامل اﻻستدعاء‬


‫‪Functions That Return Class Types and the Call Operator‬‬

‫كأي عامل ‪ ،‬فإن عامل اﻻستدعاء لديه تجميعية وأولوية‪.‬‬


‫أولويته هي نفس أولوية عاملي النقطة والسهم‪ .‬وتجميعيتها لليسار‪.‬‬ ‫•‬
‫ولذلك إن أعادت دالة مؤرشاً أو مرجعاً أو كائناً من نوع فئة ‪،‬‬ ‫•‬
‫فبإمكاننا استخدام النتيجة الستدعاء عضو في الكائن الناتج‪.‬‬ ‫•‬
‫على سبيل املثال ‪ ،‬يمكننا تحديد حجم أقرص سلسلة كما يلي‪:‬‬

‫‪// call size member of string returned by shorterString‬‬


‫;)(‪auto sz = shorterString(s1, s2).size‬‬

‫نظر ًا ألن هذه العوامل تجميعية لليسار ‪ ،‬فإن نتيجة دالة أقرص_سلسلة هي املعامل األيرس لعامل النقطة‪.‬‬ ‫•‬
‫وسيقوم العامل بجلب عضو ‪ size‬للمعامل السلسلة ذاك‪.‬‬ ‫•‬
‫وهذا العضو يعد املعامل األيرس لعامل اﻻستدعاء الثاني‪.‬‬ ‫•‬

‫‪Reference Returns Are Lvalues‬‬ ‫إعادات املرجع تعد قيما ً يرسى‬

‫يعتمد ما إذا كان استدعاء دالة قيمة يرسى على نوع إعادة الدالة‪.‬‬
‫فاستدعاءات دوال تعيد مراجع تعترب قيماً يرسى ؛ بينما أنواع اإلعادة األخرى تسفر عن قيم يمىن‪.‬‬ ‫•‬
‫يمكن استخدام استدعاء دالة تقوم بإعادة مرجع بنفس طرق أي قيمة يرسى أخرى‪.‬‬ ‫•‬
‫مرجعا لغري ثابت ‪:‬‬
‫ً‬ ‫باﻷخص ‪ ،‬يمكننا تعيني نتيجة دالة تعيد‬ ‫•‬

‫)‪char &get_val(string &str, string::size_type ix‬‬


‫{‬
‫‪return str[ix]; // get_val assumes given index is valid‬‬
‫}‬
‫)(‪int main‬‬
‫{‬
‫;)"‪string s("a value‬‬
‫;‪cout << s << endl‬‬ ‫‪// prints value‬‬
‫;'‪get_val(s, 0) = 'A‬‬ ‫‪// changes s[0] to A‬‬
‫;‪cout << s << endl‬‬ ‫‪// prints A value‬‬
‫;‪return 0‬‬
‫}‬

‫قد يكون من املفاجئ رؤية استدعاء دالة على الجانب األيرس من التعيني‪.‬‬ ‫•‬
‫مع ذلك ‪ ،‬ال يوجد يشء خاص في املوضوع‪.‬‬ ‫•‬
‫القيمة املعادة هي مرجع ‪ ،‬لذا فإن االستدعاء قيمة يرسى‪.‬‬ ‫•‬
‫ومثل أي قيمة يرسى أخرى ‪ ،‬قد يظهر كمعامل أيرس لعامل التعيني‪.‬‬ ‫•‬
‫إذا كان نوع اإلعادة مرجعاً لثابت ‪ ،‬فعندئذ (كاملعتاد) قد ال نعني لنتيجة اﻻستدعاء ‪:‬‬

‫‪shorterString("hi", "bye") = "X"; // error: return value is const‬‬

‫‪List Initializing the Return Value‬‬ ‫قيمة إعادة تهيئة القائمة‬

‫في ظل املعيار الجديد ‪ ،‬يمكن للدوال إعادة قائمة ذات أقواس من قيم‪.‬‬
‫وكما هو حال أي إعادة أخرى ‪ ،‬يتم استخدام القائمة لتهيئة مؤقت يمثل معاد الدالة‪.‬‬ ‫•‬
‫إذا كانت القائمة خالية ‪ ،‬يتم تهيئة القيمة املؤقتة‪.‬‬ ‫•‬
‫عدا ذلك ‪ ،‬فإن قيمة اإلعادة تعتمد على نوع إعادة الدالة‪.‬‬ ‫•‬
‫على سبيل املثال ‪ ،‬تذكر دالة ‪ 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‬متساويني‪.‬‬
‫ً‬ ‫سنعيد‬ ‫•‬
‫نوعا مدمجاً ‪ ،‬ربما تحتوي قائمة اﻷقواس على قيمة واحدة على األكرث ‪،‬‬
‫في دالة تعيد ً‬ ‫•‬
‫مضي ًقا‪.‬‬
‫ّ‬ ‫ويجب أال تتطلب هذه القيمة تحوياًل‬ ‫•‬
‫فستعرف الفئة نفسها كيفية استخدام املهئي‪.‬‬
‫ّ‬ ‫وإن قامت دالة بإعادة نوع فئة ‪،‬‬ ‫•‬

‫‪Return from main‬‬ ‫اﻹعادة من الدالة ‪main‬‬

‫هناك استثناء لقاعدة تنص على أنه يجب أن تعيد الدالة ذات نوع اإلعادة غري الفارغة (أن تعيد) قيمة ما ‪:‬‬
‫حيث يُسمح لدالة ‪ 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‬‬

‫‪Recursion‬‬ ‫اﻻستدعاء الذاتي‬

‫الدالة اليت تستدعي نفسها ‪ ،‬سواء بشكل مبارش أو غري مبارش ‪ ،‬هي دالة استدعاء ذاتي ‪.recursive‬‬
‫مثالً ‪ ،‬يمكننا إعادة كتابة دالة املرضوب باستخدام استدعاء ذاتي ‪:‬‬

‫‪// calculate val!, which is 1 * 2 * 3 . . . * val‬‬


‫)‪int factorial(int val‬‬
‫{‬
‫)‪if (val > 1‬‬
‫;‪return factorial(val-1) * val‬‬
‫;‪return 1‬‬
‫}‬
‫تنازليا من‬
‫ً‬ ‫في هذا التنفيذ ‪ ،‬نستدعي دالتنا ‪ factorial‬بشكل متكرر لحساب مرضوب األرقام اليت تعد‬ ‫•‬
‫القيمة األصلية في ‪.val‬‬
‫بمجرد تناقص قيمة ‪ val‬إلى ‪ ، 1‬نوقف اﻻستدعاء الذاتي بإعادة ‪.1‬‬ ‫•‬
‫دائما مسار عرب دالة االستدعاء الذاتي ال يتضمن تكرار االستدعاء ؛‬
‫ً‬ ‫يجب أن يكون هناك‬ ‫•‬
‫عدا ذلك ‪ ،‬ستتكرر الدالة "إلى األبد"‪ ،‬ما يعين أنها ستستمر في استدعاء نفسها حىت استنفاد حزم الربنامج‬ ‫•‬
‫توصف هذه الدوال أحيا ًنا بأنها تحتوي على حلقة استدعاء ذاتي ‪.recursion loop‬‬ ‫•‬
‫في حالة ‪ ، factorial‬يحدث مسار التوقف عندما تكون قيمة ‪ val‬هي ‪.1‬‬ ‫•‬
‫يتتبع الجدول التالي تنفيذ ‪ factorial‬عند تمرير القيمة ‪.5‬‬

‫‪Trace‬‬ ‫تتبع املرضوب )‪of Factorial (5‬‬

‫‪Call‬‬ ‫‪Return‬‬ ‫‪Value‬‬

‫)‪factorial(5‬‬ ‫‪factorial(4) * 5‬‬ ‫‪120‬‬

‫)‪factorial(4‬‬ ‫‪factorial(3) * 4‬‬ ‫‪24‬‬

‫)‪factorial(3‬‬ ‫‪factorial(2) * 3‬‬ ‫‪6‬‬

‫)‪factorial(2‬‬ ‫‪factorial(1) * 2‬‬ ‫‪2‬‬

‫)‪factorial(1‬‬ ‫‪1‬‬ ‫‪1‬‬

‫ملحوظة‬

‫ﻻ يجوز أن تستدعي الدالة ‪ main‬نفسها‪.‬‬

‫قسم التمارين ‪6.3.2‬‬

‫التمرين ‪: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‬؟‬

‫‪6.3.3. Returning a Pointer to an Array‬‬ ‫‪ 6٫3٫3‬إعادة مؤرش ملصفوفة‬

‫ألنه ال يمكننا نسخ مصفوفة ‪ ،‬فال يمكن لدالة إعادة مصفوفة‪ .‬مع ذلك ‪،‬‬
‫مرجعا ملصفوفة ‪ ،‬لكن يعد بناء جملة لتعريف تلك الدالة عملية مخيفة‪.‬‬
‫ً‬ ‫يمكن لدالة أن تعيد مؤرشًا أو‬ ‫•‬
‫لحسن الحظ ‪ ،‬لدينا طرق لتبسيط مثل تلك اإلعالنات‪.‬‬ ‫•‬
‫الطريقة األكرث مبارشة هي استخدام اسم نوع مستعار ‪:‬‬ ‫•‬

‫;]‪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‬‬

‫هنا ‪ arrT‬مرادف ملصفوفة من عرشة أعداد صحيحة‪.‬‬ ‫•‬


‫نعرف نوع اإلعادة كمؤرش لهذا النوع‪.‬‬
‫وألنه ال يمكننا إعادة مصفوفة ‪ ،‬فإننا ّ‬ ‫•‬
‫وبالتالي ‪ ،‬فإن ‪ func‬هي دالة تأخذ وسيطة عدد صحيح واحد وتعيد مؤرشًا ملصفوفة من عرشة أعداد‬ ‫•‬
‫صحيحة‪.‬‬

‫إعالن دالة تعيد مؤرشاً ملصفوفة‬


‫‪Declaring a Function That Returns a Pointer to an Array‬‬

‫لإلعالن عن ‪ func‬دون استخدام اسم نوع مستعار ‪ ،‬يجب أن نتذكر أن حجم مصفوفة يتبع االسم الذي يتم‬
‫تعريفه ‪:‬‬

‫;]‪int arr[10‬‬ ‫‪// arr is array of ten ints‬‬


‫;]‪int *p1[10‬‬ ‫‪// p1 is array of ten pointers‬‬
‫;‪int (*p2)[10] = &arr‬‬ ‫‪// p2 points to array of ten ints‬‬

‫كما هو حال هذه اإلعالنات ‪ ،‬إن أردنا تعريف دالة تقوم بإعادة مؤرش ملصفوفة ‪ ،‬يجب أن يتبع الحجم اسم‬ ‫•‬
‫الدالة‪.‬‬
‫أيضا‪ .‬قائمة املعلمات تسبق الحجم‪.‬‬
‫مع ذلك ‪ ،‬تتضمن الدالة قائمة معلمات تتبع االسم ً‬ ‫•‬
‫ومن ثم ‪ ،‬فإن نموذج دالة تعيد مؤرشًا ملصفوفة هو ‪:‬‬ ‫•‬

‫]‪Type (*function(parameter_list))[dimension‬‬

‫وكما هو حال أي إعالن مصفوفة آخر ‪ Type ،‬هو نوع العنارص و‪ dimension‬هو حجم املصفوفة‪.‬‬ ‫•‬
‫أما األقواس حول ()‪ )*function(parameter_list‬رضورية لنفس السبب الذي كانت مطلوبة فيه‬ ‫•‬
‫عندما عرفنا ‪.p2‬‬
‫فبدون تلك اﻷقواس ‪ ،‬سنقوم بتعريف دالة تقوم بإعادة مصفوفة من املؤرشات‪.‬‬ ‫•‬
‫كمثال ملموس ‪ ،‬يعلن الشكل التالي ‪ func‬دون استخدام اسم نوع مستعار ‪:‬‬

‫;]‪int (*func(int i))[10‬‬

‫لفهم هذا اإلعالن ‪ ،‬قد يكون من املفيد التفكري فيه على النحو التالي‪:‬‬
‫تخرب أنه يمكننا استدعاء ‪ func‬باستخدام وسيطة ‪.int‬‬ ‫)‪func(int i‬‬
‫تخرب أنه يمكننا إزالة مرجع نتيجة ذلك اﻻستدعاء‪.‬‬ ‫))‪(*func(int i‬‬
‫تخرب أن إزالة مرجع نتيجة استدعاء ‪ func‬ينتج مصفوفة بحجم عرشة‪.‬‬ ‫]‪(*func(int i))[10‬‬
‫تخرب أن نوع العنرص في تلك املصفوفة هو ‪.int‬‬ ‫]‪int(*func(int i))[10‬‬

‫‪Using a Trailing Return Type‬‬ ‫استخدام نوع إعادة متأخر‬

‫حسب املعيار الجديد ‪ ،‬هناك طريقة أخرى لتبسيط إعالن ‪: func‬‬


‫استخدام نوع إعادة متأخر ‪.trailing type‬‬ ‫•‬
‫يمكن تعريف اﻹعادات املتأخرة ألي دالة ‪،‬‬ ‫•‬
‫ولكنها أكرث فائدة للدوال ذات أنواع اإلعادة املعقدة ‪ ،‬مثل مؤرشات (أو مراجع) ملصفوفات‪.‬‬ ‫•‬
‫يتبع نوع اإلعادة املتأخر قائمة معلمات ويسبقه <‪.-‬‬ ‫•‬
‫لتشري إلى أن اإلعادة تتبع قائمة معلمات ‪ ،‬سنستخدم ‪ auto‬حيث يظهر نوع اإلعادة في العادة ‪:‬‬ ‫•‬

‫‪// func takes int argument and returns pointer to array of ten ints‬‬
‫;]‪auto func(int i) -> int(*)[10‬‬

‫ونظرًا ألن نوع اإلعادة يأتي بعد قائمة معلمات ‪،‬‬ ‫•‬
‫فمن األسهل رؤية أن ‪ func‬تعيد مؤرشًا وأن هذا املؤرش يشري ملصفوفة من عرشة أعداد صحيحة ‪.ints‬‬ ‫•‬

‫‪Using decltype‬‬ ‫استخدام نوع‪-‬معلن‬

‫بديل آخر ‪ ،‬إذا عرفنا أن املصفوفة (ــات) اليت يمكن لدالتنا إعادة مؤرش لها ‪ ،‬فيمكننا استخدام النوع‪-‬املعلن‬
‫‪ decltype‬لإلعالن عن نوع اإلعادة‪.‬‬
‫فعلى سبيل املثال ‪ ،‬تعيد الدالة التالية مؤرشًا إلحدى مصفوفتني ‪ ،‬اعتما ًدا على قيمة معلمتها ‪:‬‬

‫;}‪int odd[] = {1,3,5,7,9‬‬


‫;}‪int even[] = {0,2,4,6,8‬‬
‫‪// returns pointer to array of five int elements‬‬
‫)‪decltype(odd) *arrPtr(int i‬‬
‫{‬
‫‪return (i % 2) ? &odd : &even; // returns pointer to the array‬‬
‫}‬

‫يستخدم نوع اإلعادة لـ ‪ arrPtr‬النوع‪-‬املعلن ليخرب أن الدالة تعيد مؤرشًا لنوع أيا كان يحتوي على ‪.odd‬‬ ‫•‬
‫هذا الكائن عبارة عن مصفوفة ‪ ،‬لذا تُعيد ‪ arrPtr‬مؤرشًا ملصفوفة من خمسة ‪.ints‬‬ ‫•‬
‫تلقائيا بتحويل املصفوفة إلى نوع‬
‫ً‬ ‫الجزء الصعب الوحيد هو أننا يجب أن نتذكر أن النوع‪-‬املعلن ال يقوم‬ ‫•‬
‫املؤرش املقابل لها‪.‬‬
‫النوع الذي تم إعادته بواسطة النوع‪-‬املعلن هو نوع مصفوفة ‪،‬‬ ‫•‬
‫ويجب أن نضيف إليه * ليشري إلى أن ‪ arrPtr‬تُعيد مؤرشًا‪.‬‬ ‫•‬

‫قسم التمارين ‪6.3.3‬‬

‫التمرين ‪:6.36‬‬
‫‪ -‬اكتب اإلعالن عن دالة تقوم بإعادة مرجع ملصفوفة من عرش سالسل ‪ ،‬دون استخدام إعادة متأخر أو نوع‪-‬معلن أو‬
‫اسم نوع مستعار‪.‬‬

‫التمرين ‪:6.37‬‬
‫‪ -‬اكتب ثالثة إعالنات إضافية للدالة في التمرين السابق‪ .‬حيث يجب عليك استخدام اسم نوع مستعار في إحداها ‪،‬‬
‫‪ -‬واستخدام إعادة متأخر في الثانية ‪،‬‬
‫‪ -‬وفي الثالثة يجب أن يستخدم النوع‪-‬املعلن‪.‬‬
‫‪ -‬ما الشكل الذي تفضله وملاذا؟‬

‫التمرين ‪:6.38‬‬
‫قم بمراجعة الدالة ‪ arrPtr‬عند تشغيلها لتعيد مرجعا ملصفوفة‪.‬‬
‫‪ 6٫4‬الــــــدوال زائــــــــــدة الـتعبئــــــة‬
‫‪6.4. Overloaded Functions‬‬

‫الدوال اليت لها نفس االسم ولكن بقوائم معلمات مختلفة واليت تظهر في نفس النطاق تعرف بالدوال زائدة التعبئة ‪.‬‬
‫على سبيل املثال ‪ ،‬في الفقرة ‪ 6.2.4‬قمنا بتعريف عدة دوال تسمى ‪: print‬‬

‫;)‪void print(const char *cp‬‬


‫;)‪void print(const int *beg, const int *end‬‬
‫;)‪void print(const int ia[], size_t size‬‬

‫تؤدي هذه الدوال نفس اإلجراء العام لكنها تنطبق على أنواع مختلفة من املعلمات‪.‬‬ ‫•‬
‫بناء على نوع الوسيط الذي نمرره ‪:‬‬
‫ً‬ ‫عندما نستدعي هذه الدوال ‪ ،‬يمكن للمرتجم استنتاج الدالة اليت نريدها‬ ‫•‬

‫;}‪int j[2] = {0,1‬‬


‫;)"‪print("Hello World‬‬ ‫)*‪// calls print(const char‬‬
‫;))‪print(j, end(j) – begin(j‬‬ ‫)‪// calls print(const int*, size_t‬‬
‫;))‪print(begin(j), end(j‬‬ ‫)*‪// calls print(const int*, const int‬‬

‫تزيل زيادة التعبئة الحاجة إلى ابتكار ‪ -‬وتذكر ‪ -‬األسماء املوجودة فقط ملساعدة املرتجم على معرفة الدالة اليت يجب‬
‫استدعاءها‪.‬‬

‫ملحوظة‬

‫ال تنطبق زيادة التعبئة على الدالة ‪.main‬‬

‫‪Defining Overloaded Functions‬‬ ‫تعريف الدوال زائدة التعبئة‬

‫بناء على االسم ورقم الهاتف ورقم الحساب ‪ ..‬إلخ‪.‬‬


‫ً‬ ‫فكر في تطبيق لقاعدة بيانات به عدة دوال تبحث عن سجل‬
‫• تتيح زيادة التعبئة للدالة تعريف مجموعة من الدوال ‪ ،‬كل منها بنفس اﻻسم ‪، lookup :‬‬
‫لكنها ستختلف من حيث كيفية قيامها بالبحث‪.‬‬ ‫•‬
‫حيث يمكننا استدعاء ‪ lookup‬وتمرير قيمة من أي نوع من األنواع املتعددة‪:‬‬ ‫•‬

‫;)&‪Record lookup(const Account‬‬ ‫‪// find by Account‬‬


‫;)&‪Record lookup(const Phone‬‬ ‫‪// find by Phone‬‬
‫;)&‪Record lookup(const Name‬‬ ‫‪// find by Name‬‬
‫;‪Account acct‬‬
‫;‪Phone phone‬‬
‫;)‪Record r1 = lookup(acct‬‬ ‫‪// call version that takes Account‬‬
‫;)‪Record r2 = lookup(phone‬‬ ‫‪// call version that takes Phone‬‬
‫هنا ‪ ،‬تشرتك جميع الدوال الثالثة في نفس االسم ‪ ،‬لكن تظل مع ذلك ثالث دوال متمزية‪.‬‬ ‫•‬
‫يستخدم املرتجم نوع (أنواع) الوسيطة ملعرفة الدالة اليت يجب استدعاءها‪.‬‬ ‫•‬
‫يجب أن تختلف الدوال زائدة التعبئة في عدد أو نوع (أنواع) معلماتها‪.‬‬ ‫•‬
‫تأخذ كل دالة من الدوال املذكورة أعاله معلمة واحدة ‪ ،‬لكن لتلك املعلمات أنواع مختلفة‪.‬‬ ‫•‬
‫من الخطأ أن تختلف دالتان فقط من حيث أنواع إعادتهما‪.‬‬ ‫•‬
‫فإذا كانت قوائم معلمات دالتني متطابقتني لكن أنواع إعادتهما مختلفة ‪ ،‬فسيكون اإلعالن الثاني خطأ ‪:‬‬ ‫•‬

‫;)&‪Record lookup(const Account‬‬


‫;)&‪bool lookup(const Account‬‬ ‫‪// error: only return type is different‬‬

‫تحديد ما إذا كان نوعا معلمتان مختلفتني‬


‫‪Determining Whether Two Parameter Types Differ‬‬

‫يمكن أن تتطابق قائمتا معلمات ‪ ،‬حىت لو لم تكن متشابهة ‪:‬‬

‫‪// each pair declares the same function‬‬


‫;)‪Record lookup(const Account &acct‬‬
‫‪Record lookup(const Account&); // parameter names are ignored‬‬
‫;‪typedef Phone TelNo‬‬
‫;)&‪Record lookup(const Phone‬‬
‫‪Record lookup(const TelNo&); // TelNo and Phone are the same type‬‬

‫في الزوج األول ‪ ،‬يسمي أول إعالن معامله‪ .‬أسماء املعلمات ليست إﻻ مساعدة في التوثيق‪.‬‬ ‫•‬
‫ال تتغري قائمة املعلمات‪.‬‬ ‫•‬
‫نوعا جدي ًدا ؛ هو مرادف لـ ‪.Phone‬‬
‫أما في الزوج الثاني ‪ ،‬فيبدو أن األنواع مختلفة ‪ ،‬لكن ‪ TelNo‬ليس ً‬ ‫•‬
‫نوعا جدي ًدا‪.‬‬
‫اسما بديالً لنوع موجود ؛ لكن ال يُنئش ً‬
‫ً‬ ‫يوفر اسم النوع املستعار‬ ‫•‬
‫مستعارا ‪،‬‬
‫ً‬ ‫اسما‬
‫ً‬ ‫لذلك ‪ ،‬هناك معلمتان تختلفان فقط في أن أحدهما يستخدم‬ ‫•‬
‫واآلخر يستخدم نوعاً يتوافق معه االسم املستعار وال يختلفان‪.‬‬ ‫•‬

‫‪Overloading and const Parameters‬‬ ‫زيادة التعبئة واملعلمات الثابتة‬

‫كما رأينا في الفقرة ‪ ،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‬‬

‫‪const_cast and Overloading‬‬ ‫قولبة_ساكنة وزيادة التعبئة‬

‫الحظنا في الفقرة ‪ 3.11.4‬أن القولبة الساكنة مفيدة للغاية في سياق الدوال زائدة التعبئة‪.‬‬
‫كمثال ‪ ،‬تذكر دالة أقرص_سلسلة من الفقرة ‪: 2.3.6‬‬

‫‪// return a reference to the shorter of two strings‬‬


‫)‪const string &shorterString(const string &s1, const string &s2‬‬
‫{‬
‫;‪return s1.size() <= s2.size() ? s1 : s2‬‬
‫}‬

‫تأخذ هذه الدالة وتعيد مراجع لسلسلة ثابتة‪.‬‬ ‫•‬


‫يمكننا استدعاء الدالة على زوج وسيطات سلسلة غري ثابتة ‪،‬‬ ‫•‬
‫لكننا سنحصل على مرجع لسلسلة كنتيجة لذلك‪.‬‬ ‫•‬
‫قد نرغب في الحصول على نسخة من أقرص_سلسلة واليت من شأنها عند إعطاء وسيطات غري ثابتة أن‬ ‫•‬
‫تسفر عن مرجع عادي‪.‬‬
‫يمكننا كتابة هذا اإلصدار من دالتنا باستخدام قولبة_ساكنة ‪:‬‬ ‫•‬

‫)‪string &shorterString(string &s1, string &s2‬‬


‫{‬
‫‪auto &r = shorterString(const_cast<const string&>(s1),‬‬
‫;))‪const_cast<const string&>(s2‬‬
‫;)‪return const_cast<string&>(r‬‬
‫}‬

‫يستدعي هذا اإلصدار اإلصدار الثابت من أقرص_سلسلة عن طريق قولبة وسيطاته إلى مراجع لثوابت‪.‬‬ ‫•‬
‫تعيد هذه الدالة مرجعاً لسلسلة ثابتة ‪ ،‬واليت نعرف أنها مرتبطة بواحدة من وسيطاتنا األصلية غري‬ ‫•‬
‫الثابتة‪.‬‬
‫لذلك ‪ ،‬نعلم أنه من اآلمن قولبة هذه السلسلة إلى سلسلة عادية في الجملة ِ‬
‫أعد‪.‬‬ ‫•‬

‫‪Calling an Overloaded Function‬‬ ‫استدعاء دالة زائدة التعبئة‬

‫نعر ف مجموعة دوال زائدة التعبئة ‪ ،‬نحتاج إلى أن نكون قادرين على استدعائها بالوسيطات املناسبة‪.‬‬
‫بمجرد أن ّ‬
‫أيضا باسم دقة زيادة التعبئة) هي عملية ‪...‬‬
‫مطابقة الدالة (املعروفة ً‬ ‫•‬
‫يتم من خاللها ربط استدعاء دالة معينة بدالة محددة من مجموعة دوال زائدة التعبئة‪.‬‬ ‫•‬
‫يحدد املرتجم الدالة اليت يجب استدعاؤها ‪،‬‬ ‫•‬
‫وذلك من خالل مقارنة الوسيطات في االستدعاء باملعلمات اليت توفرها كل دالة في مجموعة زيادة التعبئة‪.‬‬ ‫•‬

‫في كثري من الحاالت ‪ -‬وعلى األرجح في معظمها ‪-‬‬


‫يكون من السهل على املربمج تحديد ما إذا كان استدعاء معني رشعياً ‪،‬‬ ‫•‬
‫وإن كان كذلك ‪ ،‬سيتم تحديد الدالة اليت سيتم استدعاءها‪.‬‬ ‫•‬
‫لكن غالبً ا ما تختلف الدوال في مجموعة زائدة التعبئة من حيث عدد الوسيطات ‪ ،‬أو تكون أنواعها غري مرتبطة‪.‬‬
‫في مثل تلك الحاالت ‪ ،‬يكون من السهل تحديد الدالة اليت يتم استدعاؤها‪.‬‬ ‫•‬
‫يمكن أن يكون تحديد الدالة اليت يتم استدعاؤها أقل وضوحاً عندما تكون للدوال زائدة التعبئة نفس عدد‬ ‫•‬
‫املعلمات وترتبط تلك املعلمات بتحويالت‪.‬‬
‫سننظر في كيفية حل املرتجم لالستدعاءات اليت تتضمن تحويالت في الفقرة ‪. 6.6‬‬ ‫•‬

‫لكن في الوقت الحالي ‪ ،‬من املهم أن ندرك أنه بالنسبة ألي استدعاء لدالة زائدة التعبئة ‪ ،‬هناك ثالث نتائج محتملة‪:‬‬
‫يجد املرتجم دالة واحدة بالضبط تعد أفضل تطابق للوسيطات الفعلية ويقوم بتوليد كود الستدعاءها‪.‬‬ ‫•‬
‫ال توجد دالة ذات معلمات تطابق الوسيطات في االستدعاء ‪،‬‬ ‫•‬
‫وهنا يُصدر املرتجم رسالة خطأ تفيد بعدم وجود تطابق‪.‬‬ ‫•‬
‫أن يكون هناك أكرث من دالة تتطابق مع عدم وجود أي تطابقات أفضل‪.‬‬ ‫•‬
‫استدعاء غامضاً‪.‬‬
‫ً‬ ‫هنا أيضا خطأ؛ إنه يعد‬ ‫•‬

‫تمارين القسم ‪6.4‬‬

‫التمرين ‪:6.39‬‬
‫‪ -‬ارشح تأثري اإلعالن الثاني في كل مجموعة من مجموعات اإلعالنات التالية‪.‬‬
‫‪ -‬حدد أيها غري رشعي ‪ ،‬إن وجد‪.‬‬
‫)‪(a‬‬ ‫;)‪int calc(int, int‬‬
‫;)‪int calc(const int, const int‬‬

‫)‪(b‬‬ ‫;)(‪int get‬‬


‫;)(‪double get‬‬

‫)‪(c‬‬ ‫;)* ‪int *reset(int‬‬


‫;)* ‪double *reset(double‬‬

‫‪6.4.1. Overloading and Scope‬‬ ‫‪ 6٫4٫1‬زيادة التعبئة والنطاق‬

‫تحذير‬

‫في العادة ‪ ،‬يعترب القيام بإعالن دالة محلياً فكرة سيئة وغري موفقة‪ .‬مع ذلك ‪،‬‬
‫ومن أجل رشح كيفية تفاعل النطاق مع زيادة التعبئة ‪ ،‬سننتهك هذه املمارسة ونقوم بإعالنات دوال محلياً‪.‬‬

‫غالب ا ما يكون خلط لدى املربمجني الجدد في يس‪ ++‬حول التفاعل بني النطاق وزيادة التعبئة‪.‬‬
‫ً‬
‫ومع ذلك ‪ ،‬فإن زيادة التعبئة ليست له خصائص خاصة فيما يتعلق بالنطاق‪:‬‬ ‫•‬
‫كالعادة ‪ ،‬إذا أعلنا عن اسم في نطاق داخلي ‪،‬‬ ‫•‬
‫فإن هذا االسم يخفي استخدامات ذلك االسم املعلن في نطاق خارجي‪.‬‬ ‫•‬
‫األسماء ﻻ تكون زائدة التعبئة عرب النطاقات‪:‬‬ ‫•‬

‫;)(‪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‬‬
‫}‬

‫لن يتفاجأ معظم القراء أن استدعاء ‪ read‬خاطئ‪.‬‬ ‫•‬


‫فعندما يعالج املرتجم استدعاء ‪ ، read‬فإنه يجد التعريف املحلي ‪.read‬‬ ‫•‬
‫هذا االسم هو متغري منطقي ‪ ،‬وال يمكننا استدعاء منطقي‪.‬‬ ‫•‬
‫وبالتالي ‪ ،‬فإن اﻻستدعاء غري رشعي‪.‬‬ ‫•‬

‫يتم استخدام نفس العملية بالضبط لحل استدعاءات ‪.print‬‬


‫يخفي إعالن )‪ print(int‬في ‪ fooBar‬اإلعالنات السابقة لـ ‪.print‬‬ ‫•‬
‫يبدو األمر كما لو أن هناك دالة ‪ print‬واحدة متاحة‪ :‬تلك اليت تأخذ معلمة ‪ int‬واحدة‪.‬‬ ‫•‬
‫عندما نستدعي ‪ ، print‬يبحث املرتجم أوال ً عن إعالن بهذا االسم‪.‬‬ ‫•‬
‫يجد اإلعالن املحلي لـ ‪ print‬الذي يأخذ عدد صحيح‪.‬‬ ‫•‬
‫بمجرد العثور على اسم ‪ ،‬يتجاهل املرتجم استخدامات هذا االسم في أي نطاق خارجي‪.‬‬ ‫•‬
‫بدال ً من ذلك ‪ ،‬يفرتض املرتجم أن اإلعالن الذي وجده هو اإلعالن الخاص باالسم الذي نستخدمه‪.‬‬ ‫•‬
‫صالحا‪.‬‬
‫ً‬ ‫ما تبقى هو معرفة ما إذا كان استخدام االسم‬ ‫•‬

‫ملحوظة‬

‫في يس‪ ، ++‬يحدث البحث عن االسم قبل فحص النوع‪.‬‬

‫يقوم أول استدعاء بتمرير سلسلة حرفية ‪،‬‬ ‫•‬


‫لكن اإلعالن الوحيد لـ ‪ print‬املتواجد في النطاق يملك معلمة عدد صحيح‪.‬‬ ‫•‬
‫وليس باﻹمكان تحويل سلسلة حرفية إلى عدد صحيح ‪ ،‬لذا فاالستدعاء خطأ‪.‬‬ ‫•‬
‫والدالة )&‪ ، print(const string‬اليت كانت ستطابق اﻻستدعاء ‪ ،‬مخفية فلن تؤخذ في االعتبار‪.‬‬ ‫•‬
‫عندما نستدعي ‪ print‬ونمرر إليها مزدوج ‪ ،‬ستتكرر العملية‪.‬‬ ‫•‬
‫سيجد املرتجم التعريف املحلي لـ )‪.print(int‬‬ ‫•‬
‫لكن يمكن تحويل وسيطة نوع مزدوج إلى عدد صحيح ‪ ،‬وبالتالي يعد االستدعاء رشعياً‪.‬‬ ‫•‬
‫ولو أعلنا )‪ print(int‬في نفس النطاق مثل دوال ‪ print‬األخرى ‪ ،‬فستكون نسخة زائدة التعبئة‪.‬‬ ‫•‬
‫في هذه الحالة ‪ ،‬سيتم حل هذه االستدعاءات بشكل مختلف ‪ ،‬ألن املرتجم سريى جميع الدوال الثالث‪:‬‬ ‫•‬

‫;)& ‪void print(const string‬‬


‫;)‪void print(double‬‬ ‫‪// overloads the print function‬‬
‫;)‪void print(int‬‬ ‫‪// another overloaded instance‬‬
‫)‪void fooBar2(int ival‬‬
‫{‬
‫;)" ‪print("Value:‬‬ ‫)& ‪// calls print(const string‬‬
‫;)‪print(ival‬‬ ‫)‪// calls print(int‬‬
‫;)‪Print(3.14‬‬ ‫)‪// calls print(double‬‬
‫}‬
‫‪ 6٫5‬مــــــــــزيات الستخــــــدامات متخصصــــــة‬
‫‪6.5. Features for Specialized Uses‬‬

‫سنغطي في هذا القسم ثالث مزيات مرتبطة بالدوال ستكون مفيدة في العديد من الربامج ‪ ،‬ولكن ليس كلها‪:‬‬
‫الوسيطات االفرتاضية ‪ ،‬ودوال ضمن‪-‬السطر ودوال التعبري الثابت ‪،‬‬ ‫•‬
‫إضافة لبعض اﻷقسام اليت تُستخدم غالبًا أثناء تصحيح األخطاء‪.‬‬ ‫•‬

‫‪6.5.1. Default Arguments‬‬ ‫‪ 6٫5٫1‬الوسيطات اﻻفرتاضية‬

‫تحتوي بعض الدوال على معلمات تُمنح قيمة معينة في معظم اﻻستدعاءات وليس كلها‪.‬‬
‫في مثل هذه الحاالت ‪ ،‬يمكننا إعالن تلك القيمة املشرتكة كوسيطة افرتاضية لدالة‪.‬‬ ‫•‬
‫يمكن استدعاء الدوال ذات الوسيطات االفرتاضية بتلك الوسيطة أو بدونها‪.‬‬ ‫•‬
‫على سبيل املثال ‪ ،‬قد نستخدم سلسلة لتمثيل محتويات نافذة‪.‬‬ ‫•‬
‫بشكل افرتايض ‪ ،‬قد نرغب في أن يكون للنافذة ارتفاع وعرض وخلفية معينة‪.‬‬ ‫•‬
‫أيضا في السماح للمستخدمني بتمرير قيم غري تلك القيم االفرتاضية‪.‬‬
‫مع ذلك ‪ ،‬قد نرغب ً‬ ‫•‬
‫الستيعاب كل من القيم االفرتاضية واملحددة ‪ ،‬نعلن عن دالتنا لتعريف النافذة على النحو التالي‪:‬‬ ‫•‬

‫;‪typedef string::size_type sz‬‬


‫;)' ' = ‪string screen(sz ht = 24, sz wid = 80, char backgrnd‬‬

‫كمهئي للمعلمة في قائمة املعلمات‪.‬‬


‫لقد وفرنا هنا قيمة افرتاضية لكل معلمة‪ .‬يتم تحديد الوسيطة االفرتاضية ُ‬ ‫•‬
‫قد نعرف القيمة االفرتاضية ملعامل واحد أو أكرث‪.‬‬ ‫•‬
‫مع ذلك ‪ ،‬إذا كانت املعلمة تحتوي على وسيطة افرتاضية ‪،‬‬ ‫•‬
‫أيضا أن تحتوي جميع املعلمات اليت تليها أيضاً على وسيطات افرتاضية‪.‬‬
‫فيجب ً‬ ‫•‬

‫‪Calling Functions with Default Arguments‬‬ ‫استدعاء دوال بوسيطة افرتاضية‬

‫إذا أردنا استخدام وسيطة افرتاضية ‪ ،‬فإننا نحذف تلك الوسيطة عندما نستدعي الدالة‪.‬‬
‫ونظرًا ألن ‪ 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,'#‬‬

‫يتم حل الوسيطات في اﻻستدعاء حسب املوضع‪.‬‬ ‫•‬


‫يتم استخدام الوسيطات األخرية (أقىص اليمني) كوسيطات افرتاضية عند االستدعاء‪.‬‬ ‫•‬
‫أيضا توفري وسيطات لالرتفاع‬
‫على سبيل املثال ‪ ،‬إللغاء اإلعداد االفرتايض لـ ‪ ، backgrnd‬يجب علينا ً‬ ‫•‬
‫والعرض‪:‬‬

‫;)'?' ‪window = screen(, ,‬‬ ‫‪// error: can omit only trailing arguments‬‬
‫;)'?'(‪window = screen‬‬ ‫)' '‪// calls screen('?',80,‬‬

‫الحظ أن االستدعاء الثاني ‪ ،‬الذي يمرر قيمة حرف واحد ‪ ،‬يعد رشعياً‪.‬‬ ‫•‬
‫لكن بالرغم من رشعيته ‪ ،‬إﻻ أنه من غري املرجح أن يكون هو املقصود‪.‬‬ ‫•‬
‫اعترب اﻻستدعاء رشعياً بسبب أن "؟" يعد حرفا ‪ ،‬ويمكن تحويل الحرف إلى نوع املعلمة املوجودة في أقىص‬ ‫•‬
‫اليسار‪.‬‬
‫هذه املعلمة عبارة عن ‪ ، string::size_type‬وهي نوع عدد صحيح بال_إشارة‪.‬‬ ‫•‬
‫في هذا االستدعاء ‪ ،‬يتم تحويل وسيطة ‪ char‬ضمناً إلى ‪ ، string::size_type‬وتمريرها كوسيطة‬ ‫•‬
‫لالرتفاع‪.‬‬
‫على أجهزتنا ‪ ،‬قيمة "؟" كسدايس عرشي ستساوي ‪ ، 0x3F‬وهي مكافئة للرقم العرشي ‪.63‬‬ ‫•‬
‫وهكذا ‪ ،‬فإن هذا االستدعاء سيمرر الرقم ‪ 63‬إلى معلمة االرتفاع‪.‬‬ ‫•‬

‫يتمثل جزء من عمل تصميم دالة باستخدام وسيطات افرتاضية في ترتيب املعلمات‬
‫بحيث تظهر تلك األقل احتمالية الستخدامها أوال ً ‪،‬‬ ‫•‬
‫بينما تظهر تلك اليت يرجح أن تستخدم القيمة االفرتاضية أخريًا‪.‬‬ ‫•‬

‫‪Default Argument Declarations‬‬ ‫إعالنات الوسيطة االفرتاضية‬

‫بالرغم من أنه من املعتاد إعالن دالة مرة واحدة داخل عنوان ‪ ،‬فمن الرشعي إعادة تعريف الدالة عدة مرات‪.‬‬
‫ومع ذلك ‪ ،‬يمكن تحديد اإلعداد االفرتايض لكل معلمة مرة واحدة فقط في نطاق معني‪ .‬بالتالي ‪،‬‬ ‫•‬
‫ﻻ يمكن ألي إعالن الحق إضافة قيمة افرتاضية إﻻ ملعامل لم يتم تحديد اﻻفرتايض له مسب ًقا من قبل‪.‬‬ ‫•‬
‫وكالعادة ‪ ،‬ال يمكن تحديد القيم االفرتاضية إال إذا كانت جميع املعلمات على اليمني بها قيم افرتاضية‬ ‫•‬
‫بالفعل‪.‬‬
‫مثالً ‪ -‬بافرتاض ‪:‬‬ ‫•‬

‫‪// no default for height or width parameters‬‬


‫;)' ' = ‪string screen(sz, sz, char‬‬

‫ال يمكننا تغيري قيمة افرتاضية معلنة بالفعل ‪:‬‬

‫‪string screen(sz, sz, char = '*'); // error: re-declaration‬‬

‫لكن يمكننا إضافة وسيطة افرتاضية على النحو التالي‪:‬‬

‫‪string screen(sz = 24, sz = 80, char); // ok: adds default arguments‬‬

‫أفضل املمارسات‬

‫ينبغي تحديد الوسيطات االفرتاضية عاد ًة مع إعالن الدالة في عنوان مناسب‪.‬‬


‫‪Default Argument Initializers‬‬ ‫مهئي الوسيطة اﻻفرتاضية‬

‫ال يجوز استخدام املتغريات املحلية كوسيطة افرتاضية‪.‬‬


‫باستثناء هذا التقييد ‪ ،‬يمكن أن تكون الوسيطة االفرتاضية أي تعبري له نوع قابل للتحويل إلى نوع املعلمة‪:‬‬

‫‪// 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,‬‬
‫}‬

‫داخل ‪ ، f2‬قمنا بتغيري قيمة ‪.def‬‬ ‫•‬


‫يمرر اﻻستدعاء لـ ‪ screen‬هذه القيمة املحدثة‪.‬‬ ‫•‬
‫أيضا عن متغري محلي يخفي ‪ wd‬الخارجي‪.‬‬
‫أعلنت الدالة ً‬ ‫•‬
‫ومع ذلك ‪ ،‬فإن االسم املحلي ‪ wd‬غري مرتبط بالوسيطة االفرتاضية اليت تم تمريرها إلى ‪.screen‬‬ ‫•‬

‫تمارين القسم ‪6.5.1‬‬


‫التمرين ‪:6.40‬‬
‫‪ -‬أي من التعريفات التالية أخطاء – إن وجد ‪ -‬؟ ملاذا ا؟‬
‫;)‪(a) int ff(int a, int b = 0, int c = 0‬‬
‫;)‪(b) char *init(int ht = 24, int wd, char bckgrnd‬‬

‫التمرين ‪: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 Functions Avoid Function Call Overhead‬‬

‫يتم توسيع تعريف الدالة املحددة على أنها ضمن‪-‬السطر "‪( "inline‬عاد ًة) في كل استدعاء‪.‬‬
‫إذا تم تعريف أقرص_سلسلة على أنها ضمن‪-‬السطر ‪ ،‬فهذا اﻻستدعاء ‪:‬‬

‫;‪cout << shorterString(s1, s2) << endl‬‬

‫(على األرجح) سيتم توسيعه أثناء التجميع إلى يشء مثل ‪:‬‬

‫;‪cout << (s1.size() < s2.size() ? s1 : s2) << endl‬‬

‫وبالتالي تتم إزالة اﻷعباء وقت التشغيل إلنشاء دالة أقرص_سلسلة‪.‬‬


‫يمكننا تعريف أقرص_سلسلة كدالة ضمن‪-‬السطر عن طريق وضع الكلمة ( ‪ ) inline‬قبل نوع إعادة الدالة‪:‬‬

‫‪// inline version: find the shorter of two strings‬‬


‫)‪inline const string &shorterString(const string &s1, const string &s2‬‬
‫{‬
‫;‪return s1.size() <= s2.size() ? s1 : s2‬‬
‫}‬

‫ملحوظة‬

‫تخصيص ضمن‪-‬السطر ما هو إﻻ طلب للمرتجم‪ .‬فقد يختار املرتجم تجاهل ذلك الطلب‪.‬‬
‫بشكل عام ‪ ،‬تهدف آلية ضمن‪-‬السطر إلى تحسني دوال خط صغرية يتم استدعاؤها بشكل متكرر‪.‬‬ ‫•‬
‫لن يتم تضمني دالة استدعاء ذاتي من قبل العديد من املرتجمات‪.‬‬ ‫•‬
‫يكاد يكون من املؤكد أنه لن يتم توسيع دالة تتكون من ‪ 75‬سطرًا على أنها ضمن‪-‬السطر‪.‬‬ ‫•‬

‫‪constexpr Functions‬‬ ‫دوال التعبري الثابت‬

‫دالة التعبري الثابت هي دالة يمكن استخدامها في تعبري ثابت‪.‬‬


‫يتم تعريف دالة تعبري ثابت مثل أي دالة أخرى ولكن يجب أن تستوفي قيو ًدا معينة‪:‬‬ ‫•‬
‫نوعا حرفياً ‪،‬‬
‫يجب أن يكون نوع اإلعادة ونوع كل معلمة فيها ً‬ ‫•‬
‫أعد واحدة بالضبط‪:‬‬‫ويجب أن تحتوي بنية الدالة على جملة ِ‬ ‫•‬

‫} ;‪constexpr int new_sz() { return 42‬‬


‫;)(‪constexpr int foo = new_sz‬‬ ‫‪// ok: foo is constant expression‬‬

‫هنا قمنا بتعريف ‪ new_sz‬كتعبري ثابت ال تأخذ أي وسيطات‪.‬‬ ‫•‬


‫يمكن للمرتجم أن يتحقق ‪ -‬في وقت الرتجمة ‪ -‬من أن استدعاء ‪ new_sz‬يعيد تعبريًا ثاب ًتا ‪،‬‬ ‫•‬
‫لذلك يمكننا استخدام ‪ new_sz‬لتهيئة متغري تعبري ثابت ‪.foo ،‬‬ ‫•‬
‫عندما يتمكن من القيام بذلك ‪ ،‬سوف يستبدل املرتجم استدعاء دالة تعبري ثابت بقيمتها الناتجة‪.‬‬ ‫•‬
‫من أجل التمكن من توسيع الدالة على الفور ‪ ،‬تكون دوال التعبري الثابت ضمن‪-‬السطر على نحو مطلق‪.‬‬ ‫•‬

‫قد تحتوي بنية دالة التعبري الثابت على جمل أخرى ؛‬


‫طاملا أن هذه الجمل ال تنئش أي إجراءات في وقت التشغيل‪ .‬على سبيل املثال ‪،‬‬ ‫•‬
‫قد تحتوي دالة تعبري ثابت على جمل خالية ‪ ،‬وأسماء مستعارة‪ ،‬وإعالنات ‪.using‬‬ ‫•‬
‫يُسمح لدالة تعبري ثابت بإعادة قيمة ليست ثابتة‪:‬‬ ‫•‬

‫‪// scale(arg) is constant expression if arg is constant expression‬‬


‫} ;‪constexpr size_t scale(size_t cnt) { return new_sz() * cnt‬‬

‫ستعيد دالة املقياس ‪ scale‬تعبريًا ثاب ًتا إذا كانت الوسيطة الخاصة بها تعبريًا ثاب ًتا ولكن ليس بخالف ذلك‪:‬‬

‫;])‪int arr[scale(2‬‬ ‫‪// ok: scale(2) is constant expression‬‬


‫;‪int i = 2‬‬ ‫‪// i is not constant expression‬‬
‫;])‪int a2[scale(i‬‬ ‫‪// error: scale(i) is not constant expression‬‬

‫عندما نمرر تعبريًا ثاب ًتا ‪ -‬مثل القيمة الحرفية ‪ - 2‬فإن اﻹعادة تكون تعبريًا ثاب ًتا‪.‬‬ ‫•‬
‫في هذه الحالة ‪ ،‬سوف يستبدل املرتجم استدعاء ‪ scale‬بالقيمة الناتجة‪.‬‬ ‫•‬
‫أما إذا استدعينا ‪ scale‬بتعبري ليس ثاب ًتا ‪ -‬مثل الكائن ‪ - int i‬فإن اﻹعادة لن تكون تعبريًا ثاب ًتا‪.‬‬ ‫•‬
‫إذا استخدمنا ‪ scale‬في سياق يتطلب تعبريًا ثاب ًتا ‪ ،‬يتحقق املرتجم أن النتيجة هي تعبري ثابت‪.‬‬ ‫•‬
‫فإن لم تكن كذلك ‪ ،‬سيصدر املرتجم رسالة خطأ‪.‬‬ ‫•‬
‫ملحوظة‬

‫دالة التعبري الثابت غري مطلوبة إلعادة تعبري ثابت‪.‬‬

‫وضع الدوال ضمن‪-‬السطر والتعبري الثابت في ملفات عنوان‬


‫‪Put inline and constexpr Functions in Header Files‬‬

‫على عكس الدوال األخرى ‪ ،‬يمكن تعريف الدوال ضمن‪-‬السطر و التعبري الثابت عدة مرات في الربنامج‪.‬‬
‫بكل اﻷحوال ‪ ،‬يحتاج املرتجم إلى التعريف ‪ -‬وليس اإلعالن فقط ‪ -‬من أجل توسيع الكود‪.‬‬ ‫•‬
‫تماما‪.‬‬
‫ً‬ ‫ومع ذلك ‪ ،‬يجب أن تتطابق جميع تعريفات ضمن‪-‬السطر أو التعبري الثابت املحددة‬ ‫•‬
‫نتيجة لذلك ‪ ،‬يتم تعريف دوال ضمن‪-‬السطر و التعبري الثابت عاد ًة في العناوين‪.‬‬ ‫•‬

‫قسم التمارين ‪6.5.2‬‬


‫التمرين ‪:6.43‬‬
‫‪ -‬أي من التعريفات والتعريفات التالية ستضعها في العنوان؟ في ملف املصدر؟‬
‫‪ -‬ارشح السبب‪.‬‬
‫}‪(a) inline bool eq(const BigInt&, const BigInt&) {...‬‬
‫;)‪(b) void putValues(int *arr, int size‬‬

‫التمرين ‪:6.44‬‬
‫‪ -‬أعد كتابة دالة ‪ isShorter‬من الفقرة ‪ 2.2.6‬لتكون ضمن‪-‬السطر‪.‬‬

‫التمرين ‪:6.45‬‬
‫‪ -‬راجع الربامج اليت كتبتها للتدريبات السابقة وحدد ما إذا كان ينبغي تحديدها على أنها ضمن‪-‬السطر‪.‬‬
‫‪ -‬إذا كان األمر كذلك ‪ ،‬فافعل ذلك‪ .‬إذا لم يكن األمر كذلك ‪ ،‬ارشح ملاذا ال ينبغي أن يكونوا ضمن‪-‬السطر‪.‬‬

‫التمرين ‪:6.46‬‬
‫‪ -‬هل من املمكن تعريف ‪ isShorter‬باعتباره تعبري ثابت؟‬
‫‪ -‬إذا كان األمر كذلك ‪ ،‬فافعل ذلك‪ .‬إذا لم يكن كذلك ‪ ،‬ارشح ملاذا ال‪.‬‬

‫‪6.5.3. Aids for Debugging‬‬ ‫‪ 6٫5٫3‬مساعدات ﻷجل تصحيح اﻷخطاء‬

‫مشابها لحراس العنوان لتنفيذ كود التصحيح املرشوط‪.‬‬


‫ً‬ ‫أسلوبا‬
‫ً‬ ‫يستخدم مربمجو يس‪ ++‬أحيا ًنا‬
‫الفكرة هي أن الربنامج سيحتوي على كود تصحيح أخطاء لن يتم تنفيذه إﻻ أثناء تطوير الربنامج‪.‬‬ ‫•‬
‫عندما يكتمل التطبيق ويكون جاه ًزا للنقل ‪ ،‬يتم إيقاف تشغيل رمز التصحيح‪.‬‬ ‫•‬
‫يستخدم هذا النهج اثنني من أقسام املعالج اﻷولي‪ :‬التوكيد ‪ assert‬و ‪.NDEBUG‬‬ ‫•‬

‫‪The assert Preprocessor Macro‬‬ ‫ماكرو معالج التوكيد اﻷولي‬

‫التوكيد ‪ assert‬عبارة عن ماكرو معالج أولي‪.‬‬


‫ماكرو املعالج اﻷولي هو متغري معالج أولي يعمل إلى حد ما مثل دالة ضمن‪-‬السطر‪.‬‬ ‫•‬
‫يأخذ ماكرو التوكيد تعبريًا واح ًدا يستخدمه كرشط ‪:‬‬ ‫•‬
‫;)تعبري ‪assert(expr‬‬

‫يقيم التعبري ‪ expr‬فإن كان خاطًئ ا (أي صفر) ‪ ،‬فيكتب التوكيد رسالة وينهي الربنامج‪.‬‬ ‫•‬
‫صفريا) ‪ ،‬ال يفعل التوكيد شيًئ ا‪.‬‬
‫ً‬ ‫صحيحا (أي ليس‬
‫ً‬ ‫وإن كان‬ ‫•‬

‫يتم تعريف ماكرو التوكيد في العنوان ‪.cassert‬‬


‫وكما رأينا ‪ ،‬تتم إدارة أسماء املعالجات اﻷولية بواسطة املعالج اﻷولي نفسه وليس بواسطة املرتجم‪.‬‬ ‫•‬
‫ونتيجة لذلك ‪ ،‬نستخدم أسماءها مبارش ًة وال نقدم إعالنا باستخدامها‪.‬‬ ‫•‬
‫وهذا يعين أننا نشري إلى ‪ ، assert‬وليس ‪ ، std::assert‬وﻻ نقدم إعالن ‪ using‬للتوكيد‪.‬‬ ‫•‬

‫وكما هو حال متغريات املعالج اﻷولي‪ ،‬يجب أن تكون أسماء املاكرو فريدة داخل الربنامج‪.‬‬
‫فال يسمح للربامج اليت تتضمن العنوان ‪ cassert‬تعريف متغري أو دالة أو كائن آخر يسمى ‪.assert‬‬ ‫•‬
‫عملياً ‪ ،‬من الجيد تجنب استخدام االسم ‪ assert‬ألغراضنا الخاصة حىت لو لم نقم بتضمني ‪.cassert‬‬ ‫•‬
‫حيث تتضمن العديد من العناوين العنوان ‪، cassert‬‬ ‫•‬
‫ما يعين أنه حىت إن لم تقم بتضمني هذا امللف بشكل مبارش ‪ ،‬فمن املحتمل أنه قد تم تضمينه في برامجك‬ ‫•‬
‫على أية حال‪.‬‬

‫غالب ا ما يتم استخدام ماكرو التوكيد لفحص رشوط "ال يمكن أن تحدث"‪ .‬فعلى سبيل املثال ‪،‬‬
‫ً‬
‫دائما ما تكون أطول‬
‫ً‬ ‫• قد يعلم برنامج يقوم بمعالجة بعض نصوص مدخالت أن جميع الكلمات اليت أدخلت‬
‫من الحد‪.‬‬
‫• قد يحتوي برنامج كهذا على جملة مثل ‪:‬‬

‫;)‪assert(word.size() > threshold‬‬

‫‪The NDEBUG Preprocessor Variable‬‬ ‫متغري املعالج اﻷولي ‪NDEBUG‬‬

‫يعتمد سلوك التوكيد على حالة متغري معالج أولي يسمى ‪.NDEBUG‬‬
‫إذا تم تعريف ‪ ، NDEBUG‬فال يفعل التوكيد شيًئ ا‪.‬‬ ‫•‬
‫افرتاضياً ‪ ،‬لم يتم تعريف ‪ ، NDEBUG‬ولذلك يقوم التوكيد افرتاضياً بإجراء فحص وقت التشغيل‪.‬‬ ‫•‬
‫يمكننا "إطفاء" تصحيح األخطاء من خالل توفري ‪ #define‬لتعريف ‪.NDEBUG‬‬ ‫•‬
‫بدال ً من ذلك ‪ ،‬توفر معظم املرتجمات خيارا في سطر األوامر يتيح لنا تعريف متغريات معالج أولي ‪:‬‬ ‫•‬

‫‪$ CC -D NDEBUG main.C # use /D with the Microsoft compiler‬‬

‫هذا السطر له نفس تأثري كتابة ‪ #define NDEBUG‬في بداية ‪.main.C‬‬ ‫•‬
‫إذا تم تعريف ‪ ، NDEBUG‬فإننا نتجنب أعباء محتملة وقت التشغيل تتضمن فحص أوضاع مختلفة‪.‬‬ ‫•‬
‫أيضا فحص وقت تشغيل‪.‬‬
‫بالطبع ‪ ،‬ال يوجد ً‬ ‫•‬
‫لذلك ‪ ،‬يجب استخدام التوكيد فقط للتحقق من أشياء ال ينبغي أن تكون ممكنة ح ًقا‪.‬‬ ‫•‬
‫ويمكن أن يكون مفي ًدا كعامل مساعد في تصحيح أخطاء الربنامج ‪،‬‬ ‫•‬
‫ولكن ال ينبغي استخدامه كبديل لعمليات التحقق من منطق وقت التشغيل ‪،‬‬ ‫•‬
‫أو التحقق من األخطاء اليت يجب أن يقوم بها الربنامج‪.‬‬ ‫•‬

‫إضافة إلى استخدام التوكيد ‪ ،‬يمكننا كتابة كود تصحيح رشطي خاص بنا باستخدام ‪.NDEBUG‬‬
‫إذا لم يتم تعريف ‪ ، NDEBUG‬يتم تنفيذ الكود بني ‪ #ifndef‬و ‪.#endif‬‬ ‫•‬
‫إذا تم تعريف ‪ ، NDEBUG‬سيتم تجاهل الكود ‪:‬‬ ‫•‬

‫)‪void print(const int ia[], size_t size‬‬


‫{‬
‫‪#ifndef NDEBUG‬‬
‫‪// _ _func_ _ is local static defined by compiler holds function's name‬‬
‫;‪cerr << _ _func_ _ << ": array size is " << size << endl‬‬
‫‪#endif‬‬
‫‪// ...‬‬

‫هنا نستخدم متغري اسمه _ _‪ _ _func‬لطباعة اسم الدالة اليت نقوم بتصحيحها‪.‬‬ ‫•‬
‫يعرف املرتجم _ _‪ _ _func‬في كل دالة‪.‬‬
‫ّ‬ ‫•‬
‫وهي عبارة عن مصفوفة ثابتة محلية من الحرف الثابت الذي يحمل اسم الدالة‪.‬‬ ‫•‬
‫وإضافة إلى _ _‪ ، _ _func‬اليت يعرفها مرتجم يس‪، ++‬‬ ‫•‬
‫يعرف املعالج اﻷولي أربعة أسماء أخرى يمكن أن تكون مفيدة في تصحيح األخطاء‪:‬‬ ‫•‬

‫_ _‪ : _ _FILE‬السلسلة الحرفية اليت تحتوي على اسم امللف‬ ‫•‬


‫_ _‪ : _ _LINE‬العدد الصحيح الحرفي الذي يحتوي على رقم السطر الحالي‬ ‫•‬
‫_ _‪ : _ _TIME‬سلسلة حرفية تحتوي على الوقت الذي تم فيه تجميع امللف‬ ‫•‬
‫_ _‪ : _ _DATE‬سلسلة حرفية تحتوي على التاريخ الذي تم فيه تجميع امللف‬ ‫•‬
‫قد نستخدم هذه الثوابت لنستعرض تقارير عن معلومات إضافية في رسائل الخطأ‪:‬‬

‫)‪if (word.size() < threshold‬‬


‫" ‪cerr << "Error:‬‬ ‫_ _‪<< _ _FILE‬‬
‫_ _‪<< " : in function " << _ _func‬‬
‫" ‪<< " at line‬‬ ‫‪<< _ _LINE_ _ << endl‬‬
‫" ‪<< " Compiled on‬‬ ‫_ _‪<< _ _DATE‬‬
‫" ‪<< " at‬‬ ‫‪<< _ _TIME_ _ << endl‬‬
‫‪<< " Word read was \"" << word‬‬
‫;‪<< "\": Length too short" << endl‬‬

‫إذا أعطينا هذا الربنامج سلسلة أقرص من الحد األدنى ‪ ،‬فسيتم إنشاء رسالة الخطأ التالية‪:‬‬

‫‪Error: wdebug.cc : in function main at line 27‬‬


‫‪Compiled on Jul 11 2012 at 20:50:03‬‬
‫‪Word read was "foo": Length too short‬‬

‫قسم التمارين ‪6.5.3‬‬


‫التمرين ‪:6.47‬‬
‫‪ -‬قم بمراجعة الربنامج الذي كتبته في التدريبات في الفقرة ‪ 6.3.2‬الذي استخدم اﻻستدعاء الذاتي لطباعة محتويات‬
‫متجه لطباعة مرشوطة ملعلومات حول تنفيذه‪ .‬على سبيل املثال ‪ ،‬يمكنك طباعة حجم املتجه في كل استدعاء‪.‬‬
‫‪ -‬قم برتجمة وتشغيل الربنامج مع تشغيل التصحيح وإيقاف تشغيله مرة أخرى‪.‬‬

‫التمرين ‪: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‬‬

‫تحديد الدوال املرشحة والقابلة للتطبيق‬


‫‪Determining the Candidate and Viable Functions‬‬

‫تحدد أول خطوة ملطابقة الدالة مجموعة الدوال زائدة التعبئة اليت يتم النظر فيها لالستدعاء‪.‬‬
‫الدوال في هذه املجموعة هي الدوال املرشحة‪.‬‬ ‫•‬
‫الدالة املرشحة هي دالة تحمل نفس اسم الدالة اليت تم استدعاؤها‬ ‫•‬
‫مرئيا عند نقطة االستدعاء‪.‬‬
‫ً‬ ‫واليت يكون اإلعالن عنها‬ ‫•‬
‫في هذا املثال ‪ ،‬توجد أربع دوال مرشحة تسمى ‪.f‬‬ ‫•‬

‫تحدد الخطوة الثانية من مجموعة الدوال املرشحة ‪:‬‬


‫تلك الدوال اليت يمكن استدعاؤها بواسطة الوسيطات في االستدعاء املحدد‪.‬‬ ‫•‬
‫الدوال املختارة هي دوال قابلة للتطبيق‪.‬‬ ‫•‬
‫ولكي تكون قابلة للتطبيق ‪ ،‬يجب أن تحتوي الدالة على نفس عدد املعلمات كما في وسيطات االستدعاء ‪،‬‬ ‫•‬
‫وأن يتطابق نوع كل وسيط مع نوع املعلمة املقابل له ‪ -‬أو يمكن تحويله إليه‪.‬‬ ‫•‬
‫بناء على عدد الوسيطات‪.‬‬
‫ً‬ ‫يمكننا حذف دالتني من الدوال املرشحة‬ ‫•‬
‫الدالة اليت ال تحتوي على معلمات والدالة اليت تحتوي على معلميت ‪ int‬غري قابلة للتطبيق لهذا االستدعاء‪.‬‬ ‫•‬
‫ومعلّ متان ‪ ،‬على التوالي‪.‬‬
‫استدعائنا له متغري واحد فقط ‪ ،‬وهذه الدوال لها صفر ُ‬ ‫•‬

‫قد تكون الدالة اليت تأخذ عدداً صحيحاً واحداً والدالة اليت تأخذ مزدوجني ‪ 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.6‬‬

‫التمرين ‪: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‬يجب أن تطبع كل دالة رسالة ممزية‪.‬‬
‫‪ -‬تحقق من إجاباتك للتمرين السابق‪.‬‬
‫‪ -‬إذا كانت إجاباتك غري صحيحة ‪ ،‬قم بدراسة هذا القسم حىت تفهم سبب خطأ إجاباتك‪.‬‬

‫‪6.6.1. Argument Type Conversions‬‬ ‫‪ 6٫6٫1‬تحويالت نوع الوسيطة‬

‫من أجل تحديد أفضل تطابق ‪ ،‬يقوم املرتجم بتصنيف التحويالت اليت يمكن استخدامها لتحويل كل وسيطة إلى نوع‬
‫معلمة مقابلة لها‪ .‬يتم تصنيف التحويالت على النحو التالي‪:‬‬
‫‪ .1‬تطابق تام‪ .‬تحدث املطابقة التامة عندما‪:‬‬
‫• تكون الوسيطة وأنواع املعلمات متطابقة‪.‬‬
‫• يتم تحويل الوسيطة من نوع مصفوفة أو دالة إلى نوع املؤرش املقابل‪.‬‬
‫• يتم إضافة قيمة ثابتة عالية املستوى إلى الوسيطة أو تجاهلها‪.‬‬
‫‪ .2‬تطابق من خالل تحويل ثابت‪.‬‬
‫‪ .3‬تطابق من خالل ترقية ‪.‬‬
‫‪ .4‬تطابق من خالل عملية حسابية أو تحويل مؤرش‪.‬‬
‫‪ .5‬تطابق من خالل تحويل من نوع الفئة‪.‬‬

‫تطابقات تتطلب ترقية أو تحويالً حسابياً‬


‫‪Matches Requiring Promotion or Arithmetic Conversion‬‬

‫تحذير‬

‫• يمكن أن تأتي الرتقية والتحويالت بني األنواع املدمجة بنتائج مفاجئة في سياق مطابقة الدوال‪.‬‬
‫نادرا ما تتضمن األنظمة املصممة جي ًدا دوال ذات معلمات وثيقة الصلة مثل‬
‫ً‬ ‫• لكن لحسن الحظ ‪،‬‬
‫تلك املوجودة في األمثلة التالية‪.‬‬

‫لتحليل استدعاء ‪،‬‬


‫دائما إلى ‪ int‬أو إلى نوع صحيح أكرب‪.‬‬
‫ً‬ ‫من املهم أن تتذكر أن أنواع العدد الصحيح الصغري يتم ترقيتها‬ ‫•‬
‫بالنظر إلى دالتني ‪ ،‬إحداهما تأخذ ‪ int‬واألخرى ‪ ، short‬سيتم استدعاء النسخة القصرية فقط على قيم‬ ‫•‬
‫من النوع ‪.short‬‬
‫وبالرغم من أن القيم الصحيحة األصغر قد تبدو أقرب إلى التطابق ‪ ،‬يتم ترقية هذه القيم إلى ‪، int‬‬ ‫•‬
‫بينما يتطلب استدعاء اإلصدار القصري تحوياًل ‪:‬‬ ‫•‬

‫;)‪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‬‬ ‫•‬
‫ونظر ًا لوجود تحويلني حسابيني محتملني ‪ ،‬فسيعد االستدعاء غامضاً‪.‬‬ ‫•‬

‫مطابقة الدالة والوسيطات الثابتة‬


‫‪Function Matching and const Arguments‬‬

‫عندما نستدعي دالة زائدة التعبئة تختلف فيما إذا كانت معلمة مرجع أو مؤرش لثابت ‪ ،‬يستخدم املرتجم ثبات‬
‫الوسيطة لتحديد الدالة اليت يجب استدعاءها ‪:‬‬

‫;)&‪Record lookup(Account‬‬ ‫‪// function that takes reference to Account‬‬


‫;)&‪Record lookup(const Account‬‬ ‫‪// new function that takes const reference‬‬
‫;‪const Account a‬‬
‫;‪Account b‬‬
‫;)‪lookup(a‬‬ ‫)&‪// calls lookup(const Account‬‬
‫;)‪lookup(b‬‬ ‫)&‪// calls lookup(Account‬‬

‫في أول استدعاء ‪ ،‬نمرر الكائن الثابت ‪ .a‬ال يمكننا ربط مرجع عادي بكائن ثابت‪.‬‬ ‫•‬
‫مرجعا لثابت‪.‬‬
‫ً‬ ‫في هذه الحالة ‪ ،‬تكون الدالة الوحيدة القابلة للتطبيق هي اإلصدار الذي يأخذ‬ ‫•‬
‫عالوة على ذلك ‪ ،‬فإن هذا االستدعاء هو مطابقة تامة للوسيطة ‪.a‬‬ ‫•‬
‫في االستدعاء الثاني ‪ ،‬نمرر كائن غري ثابت ‪ .b‬لهذا اﻻستدعاء ‪ ،‬كلتا الدالتني قابلة للتطبيق‪.‬‬ ‫•‬
‫يمكننا استخدام ‪ b‬لتهيئة مرجع لثابت أو غري ثابت‪.‬‬ ‫•‬
‫مع ذلك ‪ ،‬فإن تهيئة مرجع لثابت على كائن غري ثابت يتطلب تحويالً‪.‬‬ ‫•‬
‫اإلصدار الذي يأخذ معلمة غري ثابت هو مطابقة تامة لـ ‪ .b‬ومن ثم ‪ ،‬يفضل اإلصدار غري الثابت‪.‬‬ ‫•‬

‫تعمل معلمات املؤرش بطريقة مماثلة‪.‬‬


‫فإذا اختلفت دالتان فقط حول ما إذا كانت معلمة مؤرش لثابت أو غري ثابت ‪،‬‬ ‫•‬
‫بناء على ثبات الوسيطة‪:‬‬
‫ً‬ ‫فيمكن للمرتجم أن يمزي الدالة اليت يجب استدعاءها‬ ‫•‬
‫فإذا كانت الوسيطة مؤرشًا لثابت ‪ ،‬فسيتطابق االستدعاء مع الدالة اليت تتطلب مؤرشاً لثابت ؛‬ ‫•‬
‫عاديا‪.‬‬
‫ً‬ ‫عدا ذلك ‪ ،‬إذا كانت الوسيطة مؤرش لغري ثابت ‪ ،‬تستدعى الدالة اليت تأخذ مؤرشًا‬ ‫•‬

‫تمارين القسم ‪6.6.1‬‬

‫التمرين ‪: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‬‬

‫املؤرش للدالة هو مجرد مؤرش يشري لدالة بدال ً من أن يشري لكائن‪.‬‬


‫ومثل أي مؤرش آخر ‪ ،‬يشري مؤرش الدالة إلى نوع معني‪.‬‬ ‫•‬
‫يتم تحديد نوع الدالة من خالل نوع إعادتها وأنواع معلماتها‪.‬‬ ‫•‬
‫جزءا من نوعها‪.‬‬
‫ً‬ ‫ﻻ يعد اسم الدالة‬ ‫•‬
‫فمثال‪:‬‬ ‫•‬

‫‪// compares lengths of two strings‬‬


‫;)& ‪bool lengthCompare(const string &, const string‬‬

‫عبارة عن دالة تملك نوعاً منطقياً (مرجع لسلسلة ثابتة ‪ ،‬مرجع لسلسلة ثابتة)‪.‬‬ ‫•‬
‫ولإلعالن عن مؤرش يمكن أن يشري لهذه الدالة ‪ ،‬نعلن عن مؤرش مكان اسم الدالة ‪:‬‬ ‫•‬

‫‪// pf points to function returning bool takes two const string references‬‬
‫;)& ‪bool (*pf)(const string &, const string‬‬ ‫‪// uninitialized‬‬

‫بدءا من اسم نعلنه ‪ ،‬نرى أن ‪ pf‬يسبقها * ‪ ،‬ولذا فإن ‪ pf‬عبارة عن مؤرش‪.‬‬


‫ً‬ ‫•‬
‫إلى يمينها توجد قائمة معلمات ‪ ،‬مما يعين أن ‪ pf‬تشري لدالة‪.‬‬ ‫•‬
‫بالنظر إلى اليسار ‪ ،‬نجد أن النوع املعاد من الدالة هو منطقي‪.‬‬ ‫•‬
‫وبالتالي ‪ ،‬تشري ‪ pf‬لدالة تحتوي على مرجع لسلسلتني ثابتتني كمعلمات وتعيد قيمة منطقية‪.‬‬ ‫•‬

‫ملحوظة‬

‫األقواس حول ‪ *pf‬رضورية‪.‬‬


‫• فإن حذفناها ‪ ،‬فإننا نعلن ‪ pf‬كدالة تعيد مؤرشًا لـمنطقي ‪:‬‬
‫*‪// declares function named pf that returns bool‬‬
‫;)& ‪bool *pf(const string &, const string‬‬

‫‪Using Function Pointers‬‬ ‫استخدام مؤرشات الدالة‬

‫تلقائيا إلى مؤرش‪.‬‬


‫ً‬ ‫عندما نستخدم اسم دالة كقيمة ‪ ،‬يتم تحويل الدالة‬
‫فعلى سبيل املثال ‪ ،‬يمكننا تعيني عنوان ‪ lengthCompare‬على ‪ pf‬على النحو التالي‪:‬‬

‫‪pf = lengthCompare; // pf now points to function named lengthCompare‬‬


‫‪pf = &lengthCompare; // equivalent assignment: address-of operator is optional‬‬

‫عالوة على ذلك ‪ ،‬يمكننا استخدام مؤرش دالة الستدعاء دالة يشري إليها املؤرش‪.‬‬
:‫ ليست هناك حاجة إللغاء مرجع املؤرش‬- ‫يمكننا القيام بذلك مبارشة‬

bool b1 = pf("hello", "goodbye"); // calls lengthCompare


bool b2 = (*pf)("hello", "goodbye"); // equivalent call
bool b3 = lengthCompare("hello", "goodbye"); // equivalent call

.‫ال يوجد تحويل بني مؤرشات نوع دالة ومؤرشات نوع دالة أخرى‬ •
‫مقيم كصفر ملؤرش دالة لنشري‬
ّ ‫ أو تعبري ثابت لعدد صحيح‬nullptr ‫ يمكننا تعيني‬، ‫ كالعادة‬، ‫مع ذلك‬ •
: ‫إلى أن املؤرش ال يشري إلى أي دالة‬

string::size_type sumLength(const string&, const string&);


bool cstringCompare(const char*, const char*);
pf = 0; // ok: pf points to no function
pf = sumLength; // error: return type differs
pf = cstringCompare; // error: parameter types differ
pf = lengthCompare; // ok: function and pointer types match exactly

Pointers to Overloaded Functions ‫املؤرشات لدوال زائدة التعبئة‬

.‫ يجب أن يوضح السياق أي إصدار يتم استخدامه‬، ‫ عندما نستخدم دالة زائدة التعبئة‬، ‫كالعادة‬
‫وعندما نعلن عن مؤرش لدالة زائدة التعبئة‬

void ff(int*);
void ff(unsigned int);
void (*pf1)(unsigned int) = ff; // pf1 points to ff(unsigned)

.‫يستخدم املرتجم نوع املؤرش لتحديد الدالة زائدة التعبئة اليت يجب استخدامها‬
:‫تماما مع إحدى الدوال زائدة التعبئة‬
ً ‫يجب أن يتطابق نوع املؤرش‬

void (*pf2)(int) = ff; // error: no ff with a matching parameter list


double (*pf3)(int*) = ff;// error: return type of ff and pf3 don't match

Function Pointer Parameters ‫معلمات مؤرش الدالة‬

، ‫تماما كحال املصفوفات‬


ً
.‫ال يمكننا تعريف معلمات من نوع دالة ولكن يمكن أن يكون لدينا معلمة تمثل مؤرشًا لدالة‬ •
: ‫ ولكن سيتم التعامل معها كمؤرش‬، ‫ يمكننا كتابة معلمة تبدو كنوع دالة‬، ‫ومثل املصفوفات‬ •

// third parameter is function type and automatically treated


// as pointer to function
void useBigger(const string &s1, const string &s2,
bool pf(const string &, const string &));
// equivalent declaration: explicitly define parameter
‫‪// as pointer to function‬‬
‫‪void useBigger(const string &s1, const string &s2,‬‬
‫;))& ‪bool (*pf)(const string &, const string‬‬

‫تلقائيا إلى مؤرش‪:‬‬


‫ً‬ ‫عندما نمرر دالة كوسيطة ‪ ،‬يمكننا فعل ذلك مبارشة‪ .‬وسيتم تحويلها‬

‫‪// automatically converts function lengthCompare to pointer to function‬‬


‫;)‪useBigger(s1, s2, lengthCompare‬‬

‫كما رأينا للتو في إعالن ‪ ، useBigger‬فإن كتابة أنواع مؤرشات الدوال رسعان ما تصبح مملة‪.‬‬ ‫•‬
‫فأسماء النوع املستعارة ‪ ،‬جنبًا إلى جنب مع النوع‪-‬املعلن ‪ ،‬تسمح لنا بتبسيط كود يستخدم مؤرشات دالة ‪:‬‬ ‫•‬

‫‪// Func and Func2 have function type‬‬


‫;)&‪typedef bool Func(const string&, const string‬‬
‫;‪typedef decltype(lengthCompare) Func2‬‬ ‫‪// equivalent type‬‬

‫‪// FuncP and FuncP2 have pointer to function type‬‬


‫;)&‪typedef bool(*FuncP)(const string&, const string‬‬
‫;‪typedef decltype(lengthCompare) *FuncP2‬‬ ‫‪// equivalent type‬‬

‫هنا استخدمنا ‪ typedef‬لتعريف أنواعنا‪.‬‬ ‫•‬


‫يعترب كل من ‪ Func‬و ‪ Func2‬أنواع دالة ‪ ،‬في حني أن ‪ FuncP‬و ‪ FuncP2‬هما نوعا مؤرش‪.‬‬ ‫•‬
‫من املهم مالحظة أن نوع الدالة يُعيد نوع دالة ؛ لم يتم إجراء التحويل التلقائي إلى مؤرش‪.‬‬ ‫•‬
‫ونظرًا ألن ‪ decltype‬يُرجع نوع دالة ‪ ،‬فإذا أردنا مؤرشًا ‪ ،‬فيجب أن نضيف * بأنفسنا‪.‬‬ ‫•‬
‫يمكننا إعادة إعالن ‪ useBigger‬باستخدام أي من هذه األنواع‪:‬‬ ‫•‬

‫‪// equivalent declarations of useBigger using type aliases‬‬


‫;)‪void useBigger(const string&, const string&, Func‬‬
‫;)‪void useBigger(const string&, const string&, FuncP2‬‬

‫يعلن كال اإلعالنني عن نفس الدالة‪.‬‬ ‫•‬


‫تلقائيا بتحويل نوع الدالة اليت يمثلها ‪ Func‬إلى مؤرش‪.‬‬
‫ً‬ ‫في الحالة األولى ‪ ،‬سيقوم املرتجم‬ ‫•‬

‫‪Returning a Pointer to Function‬‬ ‫إعادة مؤرش لدالة‬

‫كحال املصفوفات ‪ ،‬ال يمكننا إعادة نوع دالة ولكن يمكننا إعادة مؤرش لنوع دالة‪.‬‬
‫وباملثل ‪ ،‬يجب أن نكتب نوع اإلعادة كنوع مؤرش ؛‬ ‫•‬
‫تلقائيا مع نوع إعادة دالة كنوع مؤرش مقابل‪.‬‬
‫ً‬ ‫لن يتعامل املرتجم‬ ‫•‬
‫أيضا وكما هو الحال مع عمليات إعادة املصفوفة ‪،‬‬
‫ً‬ ‫•‬
‫فإن أسهل طريقة لإلعالن عن دالة تعيد مؤرشًا لدالة هي باستخدام اسم نوع مستعار ‪:‬‬ ‫•‬

‫;)‪using F = int(int*, int‬‬ ‫‪// F is function type, not pointer‬‬


‫;)‪using PF = int(*)(int*, int‬‬ ‫‪// PF is pointer type‬‬

‫استخدمنا هنا إعالنات اسم نوع مستعار لتعريف ‪ F‬كنوع دالة و ‪ PF‬كمؤرش لنوع الدالة‪.‬‬ ‫•‬
‫اليشء الذي يجب مراعاته هو أنه ‪ ،‬على عكس ما يحدث للمعلمات اليت لها نوع دالة ‪،‬‬ ‫•‬
‫تلقائيا إلى نوع املؤرش‪.‬‬
‫ً‬ ‫ال يتم تحويل نوع اإلعادة‬ ‫•‬
‫يجب أن نحدد رصاحة أن نوع اإلعادة هو نوع املؤرش‪:‬‬ ‫•‬

‫‪PF f1(int); // ok: PF is pointer to function; f1 returns pointer to function‬‬


‫‪F‬‬ ‫‪f1(int); // error: F is function type; f1 can't return function‬‬
‫‪F *f1(int); // ok: explicitly specify return type is pointer to function‬‬

‫أيضا‬
‫أيضا اإلعالن عن ‪ f1‬مبارش ًة ‪ ،‬وهو ما سنفعله ً‬
‫بالطبع ‪ ،‬يمكننا ً‬

‫;)‪int (*f1(int))(int*, int‬‬

‫عند قراءة هذا اإلعالن من الداخل إلى الخارج ‪ ،‬نرى أن ‪ f1‬تملك قائمة معلمات ‪ ،‬لذا فهي دالة‪.‬‬ ‫•‬
‫يسبق ‪ * f1‬لذا تعيد مؤرشًا‪ .‬يملك نوع هذا املؤرش نفسه قائمة معلمات ‪ ،‬لذا يشري لدالة‪.‬‬ ‫•‬
‫هذه الدالة تعيد ‪.int‬‬ ‫•‬

‫من أجل االكتمال ‪ ،‬تجدر اإلشارة إلى أنه يمكننا تبسيط إعالنات دوال تعيد مؤرشات لتعمل باستخدام العائد‬
‫املتأخر ‪:‬‬

‫;)‪auto f1(int) -> int (*)(int*, int‬‬

‫استخدام التلقائي أو النوع املعلن ﻷنواع مؤرش الدالة‬


‫‪Using auto or decltype for Function Pointer Types‬‬

‫إذا كنا نعرف الدالة (الدوال) اليت نريد إعادتها ‪ ،‬فيمكننا استخدام النوع املعلن لتبسيط كتابة نوع إعادة مؤرش دالة‪.‬‬
‫على سبيل املثال ‪ ،‬لنفرتض أن لدينا دالتني ‪ ،‬كلتاهما تعيد سلسلة ‪string::size_type‬‬ ‫•‬
‫ولديها سلسلتان ثابتتان ومعلمات‪.‬‬ ‫•‬
‫يمكننا كتابة دالة ثالثة تأخذ معلمة سلسلة وتعيد مؤرشًا إلحدى الدالتني على النحو التالي‪:‬‬ ‫•‬

‫;)&‪string::size_type sumLength(const string&, const string‬‬


‫;)&‪string::size_type largerLength(const string&, const string‬‬

‫‪// depending on the value of its string parameter,‬‬


‫‪// getFcn returns a pointer to sumLength or to largerLength‬‬

‫;)& ‪decltype(sumLength) *getFcn(const string‬‬

‫الجزء الصعب الوحيد في اإلعالن عن ‪ getFcn‬هو أن نتذكر أنه عندما نطبق نوع ‪ decltype‬على دالة ‪،‬‬ ‫•‬
‫فإنه يُعيد نوع دالة ‪ ،‬وليس مؤرشًا لنوع دالة‪.‬‬ ‫•‬
‫يجب أن نضيف * لإلشارة إلى أننا نعيد مؤرشًا وليس دالة‪.‬‬ ‫•‬

‫تمارين القسم ‪6.7‬‬

‫التمرين ‪:6.54‬‬
‫‪ -‬اكتب إعالنا لدالة تأخذ معلمتني من معامالت ‪ int‬وتقوم بإعادة ‪، int‬‬
‫‪ -‬وقم بتعريف املتجه الذي تحتوي عنارصه على نوع مؤرش الدالة هذا‪.‬‬

‫التمرين ‪:6.55‬‬
‫‪ -‬اكتب أربع دوال تجمع بني قيميت ‪ int‬وتطرحهما وترضبهما وتقسمهما‪.‬‬
‫‪ -‬قم بتخزين مؤرشات إلى هذه القيم في املتجه من التمرين السابق‪.‬‬

‫التمرين ‪:6.56‬‬
‫استدع كل عنرص في املتجه واطبع نتيجته‪.‬‬
‫ملخــــــــــص الفصــــــــــل‬
‫‪Chapter Summary‬‬

‫تسمي الدوال وحدات حسابية ‪،‬‬


‫هذه الوحدات رضورية لهيكلة الربامج ‪ ،‬حىت تلك املتواضعة منها‪.‬‬ ‫•‬

‫تحتوي كل دالة على نوع إعادة ‪ ،‬واسم ‪ ،‬وقائمة (قد تكون فارغة) من املعلمات ‪ ،‬وبنية دالة‪.‬‬
‫بنية الدالة عبارة عن كتلة يتم تنفيذها عند استدعاء الدالة‪.‬‬ ‫•‬

‫عندما يتم استدعاء دالة ‪،‬‬


‫يجب أن تكون الوسيطات اليت يتم تمريرها إلى الدالة متوافقة مع أنواع معلماتها املقابلة‪.‬‬ ‫•‬

‫في يس‪ ، ++‬قد يتم زيادة تعبئة الدوال ‪:‬‬


‫حيث يتم استخدام نفس االسم لتعريف دوال مختلفة طاملا أن عدد أو أنواع املعلمات في تلك الدوال مختلفة‪.‬‬ ‫•‬

‫بناء على الوسيطات في اﻻستدعاء‪.‬‬‫ً‬ ‫تلقائيا الدالة اليت يجب استدعاؤها‬


‫ً‬ ‫يكتشف املرتجم‬
‫• يشار إلى عملية اختيار الدالة الصحيحة من مجموعة الدوال زائدة التعبئة بمطابقة الدالة‪.‬‬
‫مصطلحــــــــــات معــــــــــرفة‬
‫‪Defined Terms‬‬

‫استدعاء غامض ‪:‬‬


‫‪ambiguous call‬‬ ‫خطأ في وقت الرتجمة ينتج أثناء مطابقة الدالة عندما توفر دالتان أو أكرث تطاب ًقا جي ًدا‬
‫الستدعاء ما‪.‬‬

‫‪arguments‬‬ ‫وسيطات ‪ :‬قيم مقدمة في استدعاء دالة يتم استخدامها لتهيئة معلمات الدالة‪.‬‬

‫توكيد ‪ :‬ماكرو معالج أولي يأخذ تعبريًا واح ًدا ‪ ،‬يستخدمه كرشط‪.‬‬
‫‪assert‬‬ ‫عندما ال يتم تعريف متغري املعالج اﻷولي ‪ ، NDEBUG‬يقوم التوكيد بتقييم الحالة ‪،‬‬
‫فإذا كان الرشط خاطًئ ا ‪ ،‬يكتب رسالة وينهي الربنامج‪.‬‬

‫كائنات آلية ‪ :‬كائنات موجودة فقط أثناء تنفيذ دالة‪.‬‬


‫‪automatic‬‬
‫‪objects‬‬ ‫يتم إنشاؤها عندما يمر عنرص التحكم عرب تعريفها ويتم تدمريها في نهاية الكتلة اليت تم‬
‫تعريفها فيه‪.‬‬

‫أفضل تطابق ‪ :‬دالة مطابقة تم تحديدها من مجموعة دوال زائدة التعبئة الستدعاء ما‪.‬‬
‫في حالة وجود أفضل تطابق ‪ ،‬تكون الدالة املحددة أفضل تطابق من جميع املرشحات‬
‫‪best match‬‬
‫األخرى القادرة على االستمرار في وسيطة واحدة على األقل في اﻻستدعاء وليست أسوأ‬
‫من بقية الوسيطات‪.‬‬

‫‪call by‬‬
‫‪reference‬‬ ‫استدعاء بواسطة مرجع ‪ :‬انظر تمرير بواسطة مرجع‪.‬‬

‫‪call by value‬‬ ‫استدعاء بواسطة قيمة ‪ :‬انظر تمرير بواسطة قيمة‪.‬‬

‫دوال مرشحة ‪ :‬مجموعة من دوال يتم النظر فيها عند حل استدعاء دالة‪.‬‬
‫‪candidate‬‬
‫‪functions‬‬ ‫الدوال املرشحة هي جميع الدوال ذات االسم املستخدم في استدعاء يكون اإلعالن في‬
‫نطاقها وقت اﻻستدعاء‪.‬‬

‫دالة تعبري ثابت ‪ :‬دالة قد تعيد تعبريًا ثاب ًتا‪.‬‬


‫‪constexpr‬‬
‫ضمنيا‪.‬‬
‫ً‬ ‫دالة التعبري ثابت هي دالة ضمن‪-‬السطر‬
‫‪default‬‬ ‫وسيطة افرتاضية ‪:‬‬
‫‪argument‬‬ ‫قيمة تم تحديدها افرتاضيا الستخدامها عند حذف وسيطة في استدعاء الدالة‪.‬‬

‫‪executable‬‬ ‫ملف قابل للتنفيذ ‪:‬‬


‫‪file‬‬ ‫ملف يقوم نظام التشغيل بتنفيذه ‪ ،‬ويحتوي على كود مطابق لربامجنا‪.‬‬

‫‪function‬‬ ‫دالة ‪ :‬وحدة حسابية قابلة لالستدعاء‪.‬‬

‫‪function body‬‬ ‫بنية دالة ‪ :‬كتلة دالة تحدد إجراءات الدالة‪.‬‬

‫‪function‬‬ ‫مطابقة دالة ‪ :‬عملية للمرتجم يتم من خاللها حل استدعاء دالة زائدة التعبئة‪.‬‬
‫‪matching‬‬ ‫تتم مقارنة الوسيطات املستخدمة في االستدعاء بقائمة معلمات كل دالة زائدة التعبئة‪.‬‬

‫نموذج دالة أولي ‪ :‬إعالن دالة ‪ ،‬يتكون من االسم ونوع اإلعادة وأنواع املعلمات الخاصة‬
‫‪function‬‬
‫‪prototype‬‬ ‫بالدالة‪.‬‬
‫الستدعاء دالة ‪ ،‬يجب أن يكون قد تم اإلعالن عن نموذجها األولي قبل نقطة االستدعاء‪.‬‬

‫أسماء مخفية ‪ :‬أسماء معلنة داخل نطاق تخفي الكيانات املعلنة مسب ًقا بنفس األسماء‬
‫‪hidden names‬‬
‫املعلنة خارج هذا النطاق‪.‬‬

‫_‪Initializer‬‬ ‫قائمة‪-‬مهئي ‪:‬‬


‫‪list‬‬ ‫فئة مكتبة تمثل قائمة مفصولة بفواصل من كائنات من نوع واحد محاطة بأقواس معقوفة‪.‬‬

‫‪inline‬‬ ‫دالة ضمن‪-‬السطر ‪ :‬طلب من املرتجم أن يوسع دالة عند نقطة االستدعاء ‪،‬‬
‫‪function‬‬ ‫إن أمكن‪ .‬تتجنب الدوال املضمنة الحمل العادي الستدعاء الدوال‪.‬‬

‫معا لتشكيل برنامج قابل‬


‫ربط ‪ :‬هي خطوة تجميع حيث يتم وضع ملفات كائنات متعددة ً‬
‫‪link‬‬
‫للتنفيذ‪.‬‬

‫‪local static‬‬ ‫كائنات ساكنة محلية ‪:‬‬


‫كائنات محلية تستمر قيمتها عرب استدعاءات الدالة‪.‬‬
‫‪objects‬‬ ‫الكائنات الساكنة املحلية يتم إنشاؤها وتهيئتها قبل أن يصل التحكم إلى استخدامها ويتم‬
‫إتالفها عند انتهاء الربنامج‪.‬‬

‫‪local‬‬
‫‪variables‬‬ ‫متغريات محلية ‪ :‬متغريات معرفة داخل كتلة‪.‬‬

‫ال تطابق ‪ :‬خطأ في وقت الرتجمة ينتج أثناء مطابقة الدالة عند عدم وجود دالة ذات‬
‫‪no match‬‬
‫معلمات تطابق وسيطات استدعاء معني‪.‬‬

‫‪object code‬‬ ‫كود كائن ‪ :‬يحول املرتجم إليه كود املصدر الخاصة بنا‪.‬‬

‫ملف كائن ‪ :‬يحتوي امللف على كود كائن تم إنشاؤه بواسطة املرتجم من ملف مصدر‬
‫‪object file‬‬ ‫معني‪.‬‬
‫معا‪.‬‬
‫يتم إنشاء ملف قابل للتنفيذ من ملف كائن واحد أو أكرث بعد ربط امللفات ً‬

‫حياة كائن ‪ :‬كل كائن له عمر مرتبط‪.‬‬


‫الكائنات غري الساكنة اليت تم تعريفها داخل كتلة تتواجد من وقت مصادقة تعريفها حىت‬
‫نهاية الكتلة اليت تم تعريفها فيها‪.‬‬
‫‪object‬‬
‫يتم إنشاء الكائنات العامة أثناء بدء تشغيل الربنامج‪.‬‬
‫‪lifetime‬‬
‫يتم إنشاء الكائنات الساكنة املحلية قبل مرور املرة األولى اليت يمر فيها التنفيذ عرب تعريف‬
‫الكائن‪.‬‬
‫يتم إتالف الكائنات العامة والكائنات الساكنة املحلية عند انتهاء الدالة ‪.main‬‬

‫‪overload‬‬
‫‪resolution‬‬ ‫زيادة تعبئة دقة أو قرار ‪ :‬انظر مطابقة دالة‪.‬‬

‫‪overloaded‬‬ ‫دالة زائدة التعبئة ‪ :‬دالة لها نفس اسم دالة أخرى على األقل‪.‬‬
‫‪function‬‬ ‫يجب أن تختلف الدوال زائدة التعبئة في عدد أو نوع معلماتها‪.‬‬

‫معلمات ‪ :‬متغريات محلية معلنة داخل قائمة معلمات دالة‪.‬‬


‫‪parameters‬‬
‫تتم تهيئة املعلمات عن طريق وسيطات متوفرة في كل استدعاء دالة‪.‬‬

‫تمرير بواسطة مرجع ‪ :‬وصف كيفية تمرير وسيطات إلى معلمات نوعها مرجعي‪.‬‬
‫‪pass by‬‬
‫‪reference‬‬ ‫تعمل املعلمات املرجعية بنفس الطريقة اليت تعمل بها أي استخدام آخر للمراجع ؛‬
‫كمعلمة مرتبطة بالوسيطة املقابلة لها‪.‬‬
‫تمرير بواسطة قيمة‪ :‬كيفية تمرير وسيطة إلى معلمات من نوع غري مرجعي‪.‬‬
‫‪pass by value‬‬
‫املعلمة غري املرجعية هي نسخة من قيمة الوسيطة املقابلة لها‪.‬‬

‫ماكرو معالج أولي ‪ :‬قسم معالج أولي يترصف مثل دالة ضمن‪-‬السطر‪.‬‬
‫‪preprocessor‬‬
‫‪macro‬‬ ‫برصف النظر عن التوكيد‪ ،‬ال تستخدم برامج يس‪ ++‬الحديثة سوى القليل ج ًدا من وحدات‬
‫ماكرو املعالج األولي‪.‬‬

‫حلقة استدعاء ذاتي ‪:‬‬


‫‪recursion loop‬‬
‫وصف دالة استدعاء ذاتي تتجاهل رشط إيقاف وتستدعي نفسها حىت تنفد حزم الربنامج‪.‬‬

‫‪recursive‬‬
‫‪function‬‬ ‫دالة استدعاء ذاتي ‪ :‬تستدعي نفسها بشكل مبارش أو غري مبارش‪.‬‬

‫‪return type‬‬ ‫نوع إعادة ‪ :‬جزء من تعريف دالة يحدد نوع قيمة تعيدها الدالة‪.‬‬

‫‪separate‬‬
‫‪compilation‬‬ ‫تجميع منفصل ‪ :‬القدرة على تقسيم الربنامج إلى عدة ملفات مصدر منفصلة‪.‬‬

‫‪trailing‬‬
‫‪return type‬‬ ‫نوع إعادة متأخر ‪ :‬نوع إعادة محدد بعد قائمة املعلمات‪.‬‬

‫دوال قابلة للتطبيق ‪ :‬مجموعة فرعية من دوال مرشحة يمكن أن تتطابق مع استدعاء‬
‫‪viable‬‬ ‫معني‪.‬‬
‫‪functions‬‬ ‫تحتوي الدوال القابلة للتطبيق على نفس عدد املعلمات مثل وسيطات االستدعاء ‪،‬‬
‫ويمكن تحويل كل نوع وسيطة إلى نوع املعلمة املقابل‪.‬‬

‫العامل () ‪ :‬عامل ينفذ (يستدعي) دالة‪.‬‬


‫‪() operator‬‬ ‫يعقب اسم دالة أو مؤرش دالة األقواس ‪ ،‬واليت تتضمن قائمة وسيطات (قد تكون فارغة)‬
‫مفصولة بفواصل‪.‬‬

You might also like