C++: تحميل الدوال والمعلمات الافتراضية
آخر تحديث: 2026-08-26
أحيانًا تحتاج دالة يمكنها التعامل مع أنواع مختلفة أو أعداد مختلفة من المعلمات.
مثلًا، دالة
C++ توفر ميزتين لهذا — تحميل الدوال والمعلمات الافتراضية.
1. تحميل الدوال
(1) 1.1 ما هو تحميل الدوال؟
تحميل الدوال يسمح لك بتعريف دوال متعددة تحمل نفس الاسم، لكن بقوائم معلمات مختلفة.
| توقيع الدالة | المعنى |
|---|---|
print(int x) |
طباعة عدد صحيح |
print(double x) |
طباعة عدد فاصلة عائمة |
print(const std::string& x) |
طباعة سلسلة نصية |
عند الاستدعاء، يختار المترجم تلقائيًا الدالة الصحيحة بناءً على أنواع المعاملات.
▶ مثال 1: تحميل الدوال (صعوبة ⭐)
#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;
}
الخرج:
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 الاستخدام الأساسي
المعلمات الافتراضية تسمح لك بحذف معلمات معينة عند استدعاء دالة — سيستخدم المترجم القيم الافتراضية تلقائيًا.
#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 قواعد المعلمات الافتراضية
- القيم الافتراضية يمكن تعيينها فقط من اليمين إلى اليسار
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
- المعلمات الافتراضية يمكن تحديدها مرة واحدة فقط — في النموذج الأولي أو التعريف
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: طباعة مصفوفة بفاصل (صعوبة ⭐⭐)
#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;
}
الخرج:
1, 2, 3, 4, 5
1 - 2 - 3 - 4 - 5
5. تطبيق: دالة تسجيل بسيطة
▶ مثال 3: دالة طباعة بمستويات سجل (صعوبة ⭐⭐)
#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;
}
الخرج:
[] []
الخرج (مثال):
[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 غموض تحميل الدوال
مثال خطأ:
#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;
}
رسالة خطأ الترجمة:
error: call to 'foo' is ambiguous
إصلاح: تحويل نوع صريح:
foo(static_cast<int>('A')); // Calls foo(int)
foo(static_cast<double>('A')); // Calls foo(double)
(2) 6.2 المعلمات الافتراضية تسبب غموض التحميل
مثال خطأ:
#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++ تُجري تشويه الاسم، مشفرة معلومات نوع المعلمة في اسم الدالة، لذا يمكن للدوال التي تحمل نفس الاسم التعايش.
س: هل يمكن أن تكون المعلمات الافتراضية متغيرات؟ ج: نعم، لكن يجب أن تكون متغيرات عامة أو ساكنة (ستُغطى لاحقًا).
CPPint defaultY = 10; void foo(int x, int y = defaultY) { // Can be a variable // ... }
س: هل يحسّن تحميل الدوال كفاءة وقت التشغيل؟ ج: لا. تحميل الدوال ميزة وقت الترجمة (المترجم يختار الدالة بناءً على أنواع المعاملات)؛ كفاءة وقت التشغيل هي نفسها كاستدعاء دالة عادية. إنها تحسّن كفاءة التطوير (الكود أكثر إيجازًا وقابلية للقراءة).
📖 ملخص
- تحميل الدوال يسمح بتعريف دوال متعددة تحمل نفس الاسم بقوائم معلمات مختلفة
- المعلمات الافتراضية تسمح بحذف معلمات معينة؛ يملأ المترجم القيم الافتراضية
- المعلمات الافتراضية يمكن تعيينها فقط من اليمين إلى اليسار
- تحميل الدوال + المعلمات الافتراضية يجعلان الدوال أكثر مرونة
- انتبه لتجنب غموض التحميل
📝 تمارين
-
أساسي (صعوبة ⭐): اكتب دوال محملة
int max(int a, int b)وdouble max(double a, double b)تُرجع القيمة القصوى لعددين صحيحين وعددين فاصلة عائمة على التوالي. -
متوسط (صعوبة ⭐⭐): اكتب دالة
void drawRectangle(int width, int height, char fillChar = '*')تطبع مستطيلًا.
Input: drawRectangle(5, 3, '#')
Output:
#####
#####
#####
(عندما لا يُمرَّر fillChar، استخدم الافتراضي *)
- تحدي (صعوبة ⭐⭐⭐): اكتب برنامجًا ينفذ "آلة حاسبة متعددة الوظائف":
- استخدم تحميل الدوال لتنفيذ
add،subtract،multiply،divideلكل من نوعيintوdouble - استخدم المعلمات الافتراضية لتعيين "المنازل العشرية" (الافتراضي 2)
- اختبر جميع الدوال في
main
- تحميل الدوال: نفس الاسم، معلمات مختلفة (عدد أو أنواع مختلفة)
- حل التحميل: المترجم يطابق أفضل نسخة بناءً على أنواع المعاملات
- انتبه للغموض عند المزج بين المعلمات الافتراضية والتحميل
- لا يمكن التحميل بنوع الإرجاع وحده
- التحميل يجعل الكود أكثر بديهية، باستخدام اسم دالة واحد لأنواع مختلفة
7. 🚀 الخطوة التالية
الآن بعد أن تعلمت الاستخدام المتقدم للدوال، سنتعلم بعد ذلك النطاق والعمر (الدرس 14) — فهم أين يمكن الوصول إلى المتغيرات وكم طويل فترة وجودها، وهو مفتاح فهم سلوك البرنامج!