Menu
flag Ar iconالعربيةdown icon

المراجع مقابل المؤشرات في C++: متى تستخدم كلًّا منهما

مقارنة عملية بين المراجع والمؤشرات في C++: ما الذي تشترك فيه، وأين تختلف (إعادة الربط، القيمة الفارغة، الحساب)، وقاعدة واضحة لاختيار أيهما في الكود اليومي.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

طريقتان للإشارة إلى شيء ما

أنت تعرف بالفعل المؤشرات: متغيرات تخزّن عنوانًا وتتيح لك الوصول إلى الكائن المقيم هناك. المراجع هي الأداة الأخرى التي تمنحك إياها C++ للعمل بشكل غير مباشر مع كائن موجود. تتداخل الأداتان بما يكفي لجعل المبتدئين غالبًا غير قادرين على تمييز أيهما يختارون، لذا تضعهما هذه الصفحة جنبًا إلى جنب.

باختصار: المرجع اسم بديل. بمجرد تنفيذ int& r = x; يصبح r هو x نفسه: الكائن ذاته باسم مختلف. أما المؤشر فهو كائن منفصل يصادف أنه يحمل عنوان كائن آخر. هذا الفرق الوحيد يحكم كل شيء آخر.

المرجع اسم بديل

يجب أن يرتبط المرجع بكائن في لحظة إنشائه، ومن تلك اللحظة فصاعدًا يمسّ كل استخدام للمرجع الكائن الأصلي.

لاحظ أنه لا يوجد * لاسترجاع القيمة ولا & لـ"أخذ العنوان" عند نقطة الاستخدام؛ فأنت تقرأ alias وتكتب فيه تمامًا مثل int عادي. إن & في int& alias جزء من النوع، وليس عامل أخذ العنوان.

أين تختلفان

السلوكيات التالية هي السبب الكامل لوجود الأداتين معًا. هذا هو الجدول الذي ينبغي حفظه.

//                      reference            pointer
// must be initialized? yes                  no (but should be)
// can be null?         no                   yes (nullptr)
// can be reseated?     no                   yes
// pointer arithmetic?  no                   yes
// syntax to use it     just the name        *p  or  p->member
// taking address       &ref == &original    &p is the pointer's own address

اثنان من هذه النقاط يربكان الناس أكثر من غيرهما. أولًا، لا يمكن للمرجع أن يُعاد ربطه أبدًا؛ فإسناد قيمة إليه ينسخ قيمة داخل الكائن المُشار إليه، ولا يجعل المرجع يشير إلى شيء جديد.

أما المؤشر، في المقابل، فحرّ في أن يشير إلى مكان آخر في أي وقت:

ثانيًا، لا يمكن للمرجع أن يكون فارغًا بشكل مشروع أبدًا، بينما يمكن للمؤشر ذلك. وهذا يجعل حالة "لا قيمة" قابلة للتمثيل بمؤشر لا بمرجع، وهي خاصية ستعتمد عليها باستمرار.

الاختيار في وسائط الدوال

هنا يظهر الاختيار في أغلب الأحيان. عندما تحتاج دالة إلى قراءة كائن المُستدعي أو تعديله، تعمل الأداتان كلتاهما، لكنهما تشيران إلى نية مختلفة.

نسخة المرجع (addTax(cart)) يستحيل استدعاؤها بـ"لا شيء"، لذا داخل الدالة لا تتحقق أبدًا من القيمة الفارغة؛ فوجود الكائن مضمون. أما نسخة المؤشر (applyDiscount(&cart)) فتُعلن عند موضع الاستدعاء، عبر &، أن الوسيط قد يتغير، وتتيح للمُستدعي تمرير nullptr ليعني "غير منطبق". اختر التي تتوافق ضمانتها مع دالتك.

بالنسبة لوسائط القراءة فقط من الأنواع الكبيرة، الخيار الاصطلاحي هو const T&؛ فهو يتجنب النسخ ويَعِد بعدم التعديل. راجع وسائط الدوال لمزيد عن التمرير بالقيمة مقابل التمرير بالمرجع.

قاعدة عملية بسيطة

عندما تكون غير متأكد، اجعل المرجع خيارك الافتراضي، ولا ترتقِ إلى مؤشر إلا عندما تحتاج إلى قدرة يفتقر إليها المرجع:

  • استخدم مرجعًا عندما يكون الكائن موجودًا دائمًا ولا تتغير هويته أبدًا، وهي الحالة الشائعة لوسائط الدوال والأسماء البديلة.
  • استخدم مؤشرًا عندما يتحقق أيٌّ مما يلي:
    • "لا شيء" حالة صالحة (وسيط اختياري، بحث قد لا يجد تطابقًا)؛ فالمؤشر يمكن أن يكون nullptr.
    • تحتاج إلى الإشارة إلى كائنات مختلفة بمرور الوقت؛ فالمؤشر يمكن إعادة ربطه.
    • تدير ذاكرة في الكومة ستحررها بـ delete، أو تتنقل عبر مصفوفة باستخدام حساب المؤشرات.

إن لم ينطبق أيٌّ من ذلك، فالمرجع هو الخيار الأنظف والأكثر أمانًا، لأن المُترجِم يفرض عنك قاعدة "صالح دائمًا، ولا يُعاد ربطه أبدًا".

أخطاء شائعة ينبغي تجنبها

  • توقّع أن ref = other يعيد الربط. بدلًا من ذلك، يُسنِد قيمة داخل الكائن المُشار إليه. المراجع مرتبطة مدى الحياة؛ فإن احتجت إلى إعادة الربط، استخدم مؤشرًا.
  • إرجاع مرجع (أو مؤشر) إلى متغير محلي. الكود int& f() { int x = 5; return x; } يُرجع مرجعًا معلّقًا؛ إذ يُتلَف x عند عودة f، واستخدام النتيجة سلوك غير معرّف. الفخ نفسه يطال المؤشرات (return &x;).
  • تزييف "مرجع فارغ". كتابة int& r = *p; عندما يكون p مساويًا لـ nullptr هي سلوك غير معرّف لحظة استرجاع القيمة، لا مرجعًا "فارغًا" آمنًا. عبّر عن الاختيارية بمؤشر أو بـ std::optional.
  • اللجوء إلى مؤشر بحكم العادة. إن كان الوسيط موجودًا دائمًا ولن تعيد ربطه أبدًا، فإن المرجع يزيل صنفًا كاملًا من فحوصات القيمة الفارغة والأعطال. لا تدفع ثمن قدرات لا تستخدمها.

التالي: الذاكرة الديناميكية

حتى الآن، كان كل كائن أشرت إليه بمرجع أو بمؤشر يُنشأ تلقائيًا في المكدس. الصفحة التالية، الذاكرة الديناميكية، تتناول new وdelete: طلب الذاكرة من نظام التشغيل في وقت التشغيل، ولماذا تمتلكها المؤشرات (لا المراجع)، وكيف يؤدي نسيان تحريرها إلى التسريبات.

الأسئلة الشائعة

ما الفرق بين المرجع والمؤشر في C++؟

المرجع هو اسم بديل لكائن موجود: يجب تهيئته، ولا يمكن أن يكون فارغًا أبدًا، ولا يمكن جعله يشير إلى كائن آخر بعد ذلك. أما المؤشر فهو متغير منفصل يحمل عنوانًا: يمكن أن يكون فارغًا، ويمكن إعادة ربطه ليشير إلى مكان آخر، ويدعم حساب المؤشرات. استخدم صياغة & مع المراجع و*/-> مع المؤشرات.

متى ينبغي أن أستخدم مؤشرًا بدلًا من مرجع في C++؟

استخدم مؤشرًا عندما يكون "لا شيء" حالة صالحة (وسيط اختياري، نتيجة غير موجودة)، أو عندما تحتاج إلى إعادة ربطه ليشير إلى كائنات مختلفة بمرور الوقت، أو عندما تمتلك ذاكرة في الكومة ستحررها بـ delete. استخدم مرجعًا عندما يكون الكائن موجودًا دائمًا ولا تتغير هويته أبدًا، وهذا يغطي معظم وسائط الدوال.

هل يمكن أن يكون المرجع فارغًا (null) في C++؟

لا. المرجع الصالح يشير دائمًا إلى كائن حقيقي، لذا لا تتحقق أبدًا مما إذا كان فارغًا. إذا أنشأت مرجعًا من مؤشر فارغ تم استرجاع قيمته (int& r = *p; حيث p فارغ)، فإنك تحصل على سلوك غير معرّف، لا على مرجع فارغ. عندما تحتاج إلى التعبير عن "ربما لا شيء"، استخدم مؤشرًا أو std::optional.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن