C++: المراجع

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

المؤشرات قوية، لكنها مرهقة نوعًا ما — تحتاج لكتابة *p للتحويل، وتحتاج للحذر من المؤشرات الفارغة.

C++ أدخلت المراجع — "أسماء مستعارة" للمتغيرات وهي أكثر أمانًا وبساطة من المؤشرات.



1. ما هي المرجع؟

(1) 1.1 المراجع في الحياة اليومية

تشبيه من الحياة المقابل البرمجي
لديك لقب "أحمد" — مناداتك بأي اسم تجدك مرجع (اسم مستعار لمتغير)
الاسم على بطاقتك الشخصية مقابل اسمك اليومي المرجع والمتغير الأصلي

جوهر المرجع: المرجع هو اسم مستعار لمتغير — التعامل مع المرجع هو تعامل مع المتغير المُشار إليه.

(2) 1.2 لماذا نحتاج المراجع؟

باستخدام المؤشرات (مرهق):

▶ مثال 2: مثال برمجي (صعوبة ⭐)

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

void modifyValue(int* x) {
 *x = 100; // Need dereference
}

int main() {
 int a = 5;
 modifyValue(&a); // Need to pass address
 std::cout << a << std::endl; // 100
 return 0;
}

الخرج:

TEXT 📖 للعرض فقط
(Program output)

باستخدام المراجع (بسيط):

CPP
#include <iostream>

void modifyValue(int& x) { // x Is a reference
 x = 100; // Use directly,No dereference needed
}

int main() {
 int a = 5;
 modifyValue(a); // Looks like pass by value,Actually pass by reference
 std::cout << a << std::endl; // 100
 return 0;
}


2. تعريف المرجع وتهيئته

(1) 2.1 تعريف مرجع

الصياغة:

TEXT 📖 للعرض فقط
Type& Reference name = originalVariable;

مثال:

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

int main() {
 int x = 5;
 int& ref = x; // ref is alias of x
 
 std::cout << "x = " << x << std::endl; // 5
 std::cout << "ref = " << ref << std::endl; // 5
 
 ref = 100; // modifying ref modifies x
 std::cout << "After modification x = " << x << std::endl; // 100
 
 return 0;
}

💡 نقطة مهمة: المراجع يجب تهيئتها — لا يمكنك التعريف أولًا ثم الإسناد لاحقًا.


(2) 2.2 المراجع مقابل المؤشرات

المقارنة المرجع المؤشر
الصياغة int& ref = x; int* p = &x;
يمكن أن يكون فارغًا؟ ❌ لا (يجب التهيئة) ✅ نعم (nullptr)
يحتاج تحويل؟ ❌ لا (يُستخدم مباشرة) ✅ نعم (*p)
يمكن إعادة الربط؟ ❌ لا (بمجرد الربط، لا يمكن تغييره) ✅ نعم (يمكن الإشارة لمتغير آخر)
التوصية ⭐⭐⭐⭐⭐ (فضّل) ⭐⭐⭐ (استخدم عند الحاجة)


3. المراجع كمعلمات دالة

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

CPP
#include <iostream>

// Use reference parameters to "return" multiple values
void minMax(int arr, int length, int& min, int& max) {
 min = arr[0];
 max = arr[0];
 
 for (int i = 1; i < length; i++) {
 if (arr[i] < min) {
 min = arr[i];
 }
 if (arr[i] > max) {
 max = arr[i];
 }
 }
}

int main() {
 int arr[5] = {3, 1, 4, 1, 5};
 int min, max;
 
 minMax(arr, 5, min, max); // min and max will be modified
 
 std::cout << "Minimum: " << min << std::endl; // 1
 std::cout << "Maximum: " << max << std::endl; // 5
 
 return 0;
}

💡 نقطة مهمة: معلمات المرجع تتيح للدوال "إرجاع" قيم متعددة (بتعديل عدة معلمات مرجعية).


(2) 3.2 المراجع الثابتة (الأكثر استخدامًا)

إذا كنت لا تريد أن تعدّل الدالة المعلمة، لكنك لا تزال تريد تجنب تكلفة النسخ، استخدم مرجعًا ثابتًا.

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

void printName(const std::string& name) { // const reference: Cannot modify name
 std::cout << "Name: " << name << std::endl;
 // name = "Alice"; // ❌ Error: Cannot modify const reference
}

int main() {
 std::string myName = "MOTO";
 printName(myName); // No need to copy entire string, more efficient
 return 0;
}

💡 القاعدة الذهبية: فضّل المراجع الثابتة لمعلمات الدوال — توازن بين الأمان والكفاءة.



4. أخطاء المراجع الشائعة

(1) 4.1 المراجع يجب تهيئتها

CPP
#include <iostream>

int main() {
 int& ref; // ❌ Error: Reference requires initialization
 return 0;
}

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

TEXT 📖 للعرض فقط
error: 'ref' declared as reference but not initialized

(2) 4.2 المراجع لا يمكن إعادة ربطها

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

int main() {
 int x = 5;
 int y = 10;
 int& ref = x;
 
 ref = y; // ❌ This is NOT rebinding! This changes x's value to y's value
 std::cout << "x = " << x << std::endl; // 10
 std::cout << "y = " << y << std::endl; // 10
 
 return 0;
}

💡 نقطة مهمة: بمجرد ربط مرجع بمتغير، لا يمكن إعادة ربطه بمتغير آخر.



5. تطبيق: إرجاع قيم متعددة

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

CPP
#include <iostream>

// Use reference parameters to "return" multiple values
void divide(int a, int b, int& quotient, int& remainder) {
 quotient = a / b;
 remainder = a % b;
}

int main() {
 int a = 17, b = 5;
 int q, r;
 
 divide(a, b, q, r); // q and r will be modified
 
 std::cout << a << " / " << b << " = " << q << " ... " << r << std::endl;
 // Output:17 / 5 = 3 ... 2
 
 return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
 /  =  ... 

▶ مثال 3: المرجع كقيمة إرجاع دالة (صعوبة ⭐)

CPP
#include <iostream>
#include <string>

std::string name = "Zhang San"; // Global variable

// Return reference (not value)
std::string& getName() {
    return name; // Returns a reference to name
}

int main() {
    getName() = "Li Si"; // Modify global variable
    
    std::cout << "name = " << name << std::endl;
    
    return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
name = 
💡 نصيحة: إرجاع مرجع من دالة يتيح استخدام القيمة المرجعة كقيمة يمنى. لكن احذر: لا تُرجع أبدًا مرجعًا لمتغير محلي!


❓ أسئلة شائعة

س: ما الفرق الحقيقي بين المراجع والمؤشرات؟ ج:> - المراجع هي أسماء مستعارة للمتغيرات؛ المؤشرات هي متغيرات تخزن عناوين > - المراجع يجب تهيئتها؛ المؤشرات يمكن أن تكون nullptr > - المراجع لا يمكن إعادة ربطها؛ المؤشرات يمكن > - فضّل المراجع إلا إذا كنت تحتاج تمثيل "لا شيء" (فاستخدم مؤشرات) س: لماذا لا يمكن تمرير المصفوفات بالمرجع كمعلمات؟ ج: يمكن ذلك، لكن صياغة مراجع المصفوفات معقدة.

نهج أبسط: استخدم std::array أو std::vector (ستُغطى لاحقًا).

س: هل تحسن المراجع كفاءة التشغيل؟ ج: لا. المراجع تُنفَّذ عادةً كمؤشرات تحت السطح، لذا كفاءة التشغيل متطابقة.

ما تحسنه هو كفاءة التطوير (الكود أنظف، أقل عرضة للأخطاء).


س: ما أهم شيء في المراجع؟ ج: افهم المفاهيم الأساسية أولًا، ثم عززها من خلال أمثلة التطبيق.


📖 ملخص


📝 تمارين

  1. أساسي (صعوبة ⭐): اكتب دالة void addTen(int& x) تضيف 10 إلى المتغير الممرر. اختبرها في main.

  2. متوسط (صعوبة ⭐⭐): اكتب دالة void swap(int& a, int& b) تبدّل قيمتي متغيرين. اختبرها في main.

  3. تحدي (صعوبة ⭐⭐⭐): اكتب برنامجًا ينفذ "فرز ثلاثة أعداد":

  4. اكتب دالة void sortThree(int& a, int& b, int& c) تفرز ثلاثة أعداد تصاعديًا

  5. في main، دع المستخدم يدخل ثلاثة أعداد، استدعِ sortThree، ثم أخرج النتيجة المرتبة

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

الآن بعد أن تعلمت المراجع، لنتابع إلى تخصيص الذاكرة الديناميكي (الدرس 26) — إدارة الذاكرة يدويًا بـ new و delete، وفهم الفرق بين المكدس والكومة.

Web-Tutorial.com

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

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

100%