C++: تحميل الدوال والمعلمات الافتراضية

آخر تحديث: 2026-08-26

أحيانًا تحتاج دالة يمكنها التعامل مع أنواع مختلفة أو أعداد مختلفة من المعلمات.

مثلًا، دالة print يمكنها طباعة أعداد صحيحة وأعداد فاصلة عائمة وسلاسل نصية.

C++ توفر ميزتين لهذا — تحميل الدوال والمعلمات الافتراضية.


1. تحميل الدوال

(1) 1.1 ما هو تحميل الدوال؟

تحميل الدوال يسمح لك بتعريف دوال متعددة تحمل نفس الاسم، لكن بقوائم معلمات مختلفة.

توقيع الدالة المعنى
print(int x) طباعة عدد صحيح
print(double x) طباعة عدد فاصلة عائمة
print(const std::string& x) طباعة سلسلة نصية

عند الاستدعاء، يختار المترجم تلقائيًا الدالة الصحيحة بناءً على أنواع المعاملات.

▶ مثال 1: تحميل الدوال (صعوبة ⭐)

TEXT 📖 للعرض فقط
#include <iostream>
#include <string>

void print(int x) {
 std::cout << "Integer: " << x << std::endl;
}

void print(double x) {
 std::cout << "Float: " << x << std::endl;
}

void print(const std::string& x) {
 std::cout << "String: " << x << std::endl;
}

int main() {
 print(5); // Calls print(int)
 print(3.14); // Calls print(double)
 print("Hello"); // Calls print(const char*)
 
 return 0;
}

الخرج:

TEXT 📖 للعرض فقط
Integer: 5
Float: 3.14
String: Hello

⚠️ نقطة مهمة: شرط تحميل الدوال هو أن قائمة المعلمات يجب أن تختلف (أنواع مختلفة، عدد مختلف، أو ترتيب مختلف). نوع الإرجاع المختلف وحده لا يشكل تحميلًا.



2. قواعد تحميل الدوال

(1) 2.1 ما الذي يُحسب كـ "قائمة معلمات مختلفة"؟

الاختلاف المثال
أنواع مختلفة void foo(int x) مقابل void foo(double x)
عدد مختلف void foo(int x) مقابل void foo(int x, int y)
ترتيب مختلف void foo(int x, double y) مقابل void foo(double x, int y)

(2) 2.2 ما لا يُحسب كتحميل؟

الحالة المثال النتيجة
نوع الإرجاع مختلف فقط int foo() مقابل double foo() ❌ خطأ ترجمة: إعادة تعريف
أسماء المعلمات مختلفة فقط void foo(int x) مقابل void foo(int y) ❌ خطأ ترجمة: إعادة تعريف


3. المعلمات الافتراضية

(1) 3.1 الاستخدام الأساسي

المعلمات الافتراضية تسمح لك بحذف معلمات معينة عند استدعاء دالة — سيستخدم المترجم القيم الافتراضية تلقائيًا.

CPP
#include <iostream>

void print(int x, int y = 10) {
 std::cout << "x = " << x << ", y = " << y << std::endl;
}

int main() {
 print(5); // Only pass one argument, y uses default value 10
 print(5, 20); // Pass two arguments, y is changed to 20
 
 return 0;
}

(2) 3.2 قواعد المعلمات الافتراضية

  1. القيم الافتراضية يمكن تعيينها فقط من اليمين إلى اليسار
CPP
void foo(int a, int b = 10, int c = 20); // ✅ Correct
void bar(int a = 5, int b, int c); // ❌ Error: a has default but b doesn't
  1. المعلمات الافتراضية يمكن تحديدها مرة واحدة فقط — في النموذج الأولي أو التعريف
CPP
int add(int a, int b = 10); // Specify default in the prototype

int main() {
std::cout << add(5) << std::endl;
return 0;
}

int add(int a, int b) { // ✅ Don't write the default again in the definition
return a + b;
}


4. تحميل الدوال + المعلمات الافتراضية

تحميل الدوال والمعلمات الافتراضية يمكن استخدامهما معًا لجعل الدوال أكثر مرونة.

▶ مثال 2: طباعة مصفوفة بفاصل (صعوبة ⭐⭐)

TEXT 📖 للعرض فقط
#include <iostream>
#include <string>

void printArray(int arr, int length) {
 printArray(arr, length, ", ");
}

void printArray(int arr, int length, const std::string& delimiter) {
 for (int i = 0; i < length; i++) {
 std::cout << arr[i];
 if (i < length - 1) {
 std::cout << delimiter;
 }
 }
 std::cout << std::endl;
}

int main() {
 int arr[5] = {1, 2, 3, 4, 5};
 
 printArray(arr, 5); // Use default delimiter ", "
 printArray(arr, 5, " - "); // Use custom delimiter " - "
 
 return 0;
}

الخرج:

TEXT 📖 للعرض فقط
1, 2, 3, 4, 5
1 - 2 - 3 - 4 - 5


5. تطبيق: دالة تسجيل بسيطة

▶ مثال 3: دالة طباعة بمستويات سجل (صعوبة ⭐⭐)

CPP
#include <iostream>
#include <string>
#include <ctime>

enum LogLevel {
 INFO,
 WARNING,
 ERROR
};

std::string getCurrentTime() {
 std::time_t now = std::time(nullptr);
 std::tm* tm = std::localtime(&now);
 char buffer[80];
 std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm);
 return std::string(buffer);
}

void log(const std::string& message) {
 log(message, INFO);
}

void log(const std::string& message, LogLevel level) {
 std::string levelStr;
 if (level == INFO) {
 levelStr = "INFO";
 } else if (level == WARNING) {
 levelStr = "WARNING";
 } else {
 levelStr = "ERROR";
 }
 
 std::cout << "[" << getCurrentTime() << "] [" << levelStr << "] " << message << std::endl;
}

int main() {
 log("Program started");
 log("Insufficient memory", WARNING);
 log("File open failed", ERROR);
 
 return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
[] [] 

الخرج (مثال):

TEXT 📖 للعرض فقط
[2026-06-28 22:30:00] [INFO] Program started
[2026-06-28 22:30:01] [WARNING] Insufficient memory
[2026-06-28 22:30:02] [ERROR] File open failed


6. الأخطاء الشائعة

(1) 6.1 غموض تحميل الدوال

مثال خطأ:

TEXT 📖 للعرض فقط
#include <iostream>

void foo(int x) {
 std::cout << "foo(int)" << std::endl;
}

void foo(double x) {
 std::cout << "foo(double)" << std::endl;
}

int main() {
 foo(5); // ✅ Calls foo(int)
 foo(3.14); // ✅ Calls foo(double)
 foo('A'); // ❌ Ambiguous: char can convert to int or double
 return 0;
}

رسالة خطأ الترجمة:

TEXT 📖 للعرض فقط
error: call to 'foo' is ambiguous

إصلاح: تحويل نوع صريح:

CPP
foo(static_cast<int>('A')); // Calls foo(int)
foo(static_cast<double>('A')); // Calls foo(double)

(2) 6.2 المعلمات الافتراضية تسبب غموض التحميل

مثال خطأ:

CPP
#include <iostream>

void foo(int x) {
 std::cout << "foo(int)" << std::endl;
}

void foo(int x, int y = 10) {
 std::cout << "foo(int, int)" << std::endl;
}

int main() {
 foo(5); // ❌ Ambiguous: can call foo(int) or foo(int, int) with default
 return 0;
}

إصلاح: أزل أحد التحميلات، أو غيّر نوع معلمة.


❓ أسئلة شائعة

س: ما الفرق بين تحميل الدوال والدوال بأسلوب C؟ ج: C لا يدعم تحميل الدوال لأن قواعد تسمية مترجم C بسيطة (أساسًا اسم الدالة نفسه). مترجمات C++ تُجري تشويه الاسم، مشفرة معلومات نوع المعلمة في اسم الدالة، لذا يمكن للدوال التي تحمل نفس الاسم التعايش.

س: هل يمكن أن تكون المعلمات الافتراضية متغيرات؟ ج: نعم، لكن يجب أن تكون متغيرات عامة أو ساكنة (ستُغطى لاحقًا).

CPP
int defaultY = 10;

void foo(int x, int y = defaultY) { // Can be a variable
// ...
}

س: هل يحسّن تحميل الدوال كفاءة وقت التشغيل؟ ج: لا. تحميل الدوال ميزة وقت الترجمة (المترجم يختار الدالة بناءً على أنواع المعاملات)؛ كفاءة وقت التشغيل هي نفسها كاستدعاء دالة عادية. إنها تحسّن كفاءة التطوير (الكود أكثر إيجازًا وقابلية للقراءة).


📖 ملخص


📝 تمارين

  1. أساسي (صعوبة ⭐): اكتب دوال محملة int max(int a, int b) وdouble max(double a, double b) تُرجع القيمة القصوى لعددين صحيحين وعددين فاصلة عائمة على التوالي.

  2. متوسط (صعوبة ⭐⭐): اكتب دالة void drawRectangle(int width, int height, char fillChar = '*') تطبع مستطيلًا.

TEXT 📖 للعرض فقط
Input: drawRectangle(5, 3, '#')
Output:
#####
#####
#####

(عندما لا يُمرَّر fillChar، استخدم الافتراضي *)

  1. تحدي (صعوبة ⭐⭐⭐): اكتب برنامجًا ينفذ "آلة حاسبة متعددة الوظائف":
  2. استخدم تحميل الدوال لتنفيذ add، subtract، multiply، divide لكل من نوعي int وdouble
  3. استخدم المعلمات الافتراضية لتعيين "المنازل العشرية" (الافتراضي 2)
  4. اختبر جميع الدوال في main

7. 🚀 الخطوة التالية

الآن بعد أن تعلمت الاستخدام المتقدم للدوال، سنتعلم بعد ذلك النطاق والعمر (الدرس 14) — فهم أين يمكن الوصول إلى المتغيرات وكم طويل فترة وجودها، وهو مفتاح فهم سلوك البرنامج!

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%