الدوال في C++: المراجع والقيم الافتراضية و Lambda
الدوال تقسّم البرنامج الكبير إلى قطع صغيرة واضحة، كل قطعة تؤدي مهمة واحدة. في C++ أخذت الدوال كل ما في C، ثم أضافت ميزات تجعلها أقوى وأسهل: دوال بنفس الاسم، وقيم افتراضية للمعاملات، والمراجع التي تغنيك في أغلب الأحيان عن المؤشرات المعقدة، والدوال المجهولة.
تعريف دالة
Section titled “تعريف دالة”#include <iostream>using namespace std;
void greet() { cout << "أهلاً بك في C++!" << endl;}
int add(int a, int b) { return a + b;}
int main() { greet(); cout << add(3, 4) << endl; return 0;}أهلاً بك في C++!7- نوع القيمة المرجعة قبل الاسم، و
voidتعني «لا تُرجع شيئاً». - المعاملات بأنواعها بين القوسين، و
returnتُرجع القيمة وتُنهي الدالة. - كما في C، إذا عرّفت الدالة بعد
main، تحتاج نموذجاً أولياً (Prototype) قبلmain. لذلك وضعنا الدوال في الأعلى هنا.
التحميل الزائد (Overloading)
Section titled “التحميل الزائد (Overloading)”يمكن أن يكون لأكثر من دالة نفس الاسم، إذا اختلفت المعاملات:
int add(int a, int b) { return a + b;}
double add(double a, double b) { return a + b;}add(3, 4) تستدعي النسخة الأولى وتُرجع 7، و add(2.5, 1.5) تستدعي الثانية وتُرجع 4. المترجم يختار حسب أنواع ما ترسله. (لاحظ أن cout تطبع 4 وليس 4.0، لأنها تحذف الأصفار غير الضرورية.)
القيم الافتراضية
Section titled “القيم الافتراضية”string makeTea(int sugar = 2) { return "شاي بـ " + to_string(sugar) + " ملاعق سكر";}cout << makeTea() << endl;cout << makeTea(0) << endl;شاي بـ 2 ملاعق سكرشاي بـ 0 ملاعق سكرالمعاملات ذات القيم الافتراضية يجب أن تكون في آخر القائمة.
التمرير بالقيمة والتمرير بالمرجع
Section titled “التمرير بالقيمة والتمرير بالمرجع”هذه أهم فكرة في الدرس:
void tryToChange(int x) { x = 100;}
void addPoints(int& score, int bonus) { score += bonus;}
int main() { int value = 5; tryToChange(value); cout << value << endl;
int score = 10; addPoints(score, 5); cout << score << endl; return 0;}515- التمرير بالقيمة
int x: الدالة تستقبل نسخة. تعديلها لا يؤثر في الأصل، لذلك بقيتvalueتساوي 5. - التمرير بالمرجع
int& score: الدالة تستقبل المتغير الأصلي نفسه باسم آخر. تعديله يغيّر الأصل، فأصبحتscoreتساوي 15.
تخيّل الفرق: بالقيمة كأنك تعطي صديقك صورة من دفترك، وبالمرجع كأنك تعطيه الدفتر نفسه.
في C كنت تحتاج المؤشرات و & و * لتحقيق هذا. في C++ المرجع أبسط: تكتب & في المعامل فقط، وتستخدم المتغير داخل الدالة كأي متغير عادي.
const&: سرعة بلا مخاطرة
Section titled “const&: سرعة بلا مخاطرة”ماذا لو كان المعامل نصاً طويلاً أو قائمة كبيرة؟ نسخه بطيء. وتمريره بالمرجع يعرّضه للتعديل بالخطأ. الحل: مرجع ثابت:
void printName(const string& name) { cout << "الاسم: " << name << endl;}printName("منى") تطبع «الاسم: منى». الدالة تقرأ الأصل دون نسخ، و const يمنعها من تعديله. هذا هو الأسلوب المعتاد لتمرير النصوص والكائنات الكبيرة في C++.
الدوال المجهولة (Lambda)
Section titled “الدوال المجهولة (Lambda)”يمكنك تعريف دالة صغيرة داخل دالة أخرى، وتخزينها في متغير:
auto square = [](int x) { return x * x; };cout << square(6) << endl;36[]بداية الـ Lambda. (وما بداخل القوسين المربعين يحدد المتغيرات الخارجية التي تستطيع رؤيتها، وهو موضوع متقدم.)(int x)المعاملات، و{ ... }الكود.
ستراها كثيراً مع دوال المكتبة القياسية، كما في درس المصفوفات القادم، عندما تريد أن تمرر شرطاً صغيراً لدالة أخرى.
جرّب بنفسك
Section titled “جرّب بنفسك”#include <iostream>using namespace std;
double priceAfterDiscount(double price, double percent = 10) { return price - price * percent / 100;}
int main() { cout << priceAfterDiscount(200) << endl; cout << priceAfterDiscount(200, 25) << endl; return 0;}180150تحدٍّ: اكتب void swapValues(int& a, int& b) تبدّل قيمتي متغيرين، وقارنها بنسخة C التي تحتاج مؤشرات.
أخطاء شائعة
Section titled “أخطاء شائعة”- التوقع أن التمرير بالقيمة يغيّر الأصل: استخدم
&إذا أردت التعديل. - تمرير قيمة ثابتة لمرجع غير ثابت:
addPoints(10, 5)خطأ، لأن المرجع يحتاج متغيراً حقيقياً يمكن تعديله. - قيمة افتراضية في غير آخر المعاملات:
void f(int a = 1, int b)خطأ ترجمة.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ما قيمة x بعد استدعاء void inc(int& n) { n++; } بـ inc(x) إذا كانت x تساوي 7؟
المعامل مرجع للمتغير الأصلي، فالزيادة داخل الدالة تغيّر x نفسها.
سؤال
ما الطريقة المفضلة لتمرير string كبير لدالة تقرؤه فقط؟
const string& تتجنب النسخ، وتمنع الدالة من تعديل النص الأصلي بالخطأ.
الخلاصة
Section titled “الخلاصة”- الدوال: نوع مرجع، واسم، ومعاملات، و
voidلا تُرجع شيئاً. - Overloading: نفس الاسم بمعاملات مختلفة، والقيم الافتراضية في آخر المعاملات.
- بالقيمة
Tنسخة، وبالمرجعT&الأصل نفسه، وبالمرجع الثابتconst T&قراءة بلا نسخ. - المراجع في C++ أبسط من المؤشرات في C لتعديل المتغيرات.
- Lambda دالة صغيرة مجهولة
[](int x) { ... }.