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

const والثوابت في C++: const و constexpr و consteval

كيفية التصريح عن قيم للقراءة فقط في C++ باستخدام const، والفرق بين const و constexpr، والمؤشرات الثابتة مقابل المؤشرات إلى ثابت، ودوال الأعضاء الثابتة.

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

لماذا توجد الثوابت

الثابت قيمة تتعهّد بألّا تغيّرها أبدًا بعد ضبطها. وسم شيء ما بـ const يؤدي مهمتين في آن واحد: يوثّق نيّتك لكل من يقرأ الشيفرة، ويتيح للمترجم فرض تلك النيّة؛ فأي سطر يحاول تعديل القيمة يصبح خطأ ترجمة بدلًا من علة صامتة أثناء التشغيل.

في حين أن الكلمة المفتاحية auto تترك للمترجم استنتاج نوع المتغير، فإن const تقيّد ما يمكنك فعله بذلك المتغير. وتتكامل الاثنتان بحرية: فالصيغة const auto limit = 100; هي int للقراءة فقط.

التصريح عن قيمة const

ضع const قبل النوع. يجب تهيئة المتغير const في السطر نفسه، إذ لا توجد لحظة لاحقة يُسمح لك فيها بإسناد قيمة له.

أزِل التعليق عن الإسناد ولن يُترجَم البرنامج؛ إذ يبلّغ المترجم عن «assignment of read-only variable». وهذا هو المقصود بالضبط: يُكتشف الخطأ قبل أن يبدأ البرنامج بالعمل أصلًا.

من العادات الشائعة لدى المبتدئين والمنقولة من C استخدام #define MAX_USERS 100. تجنّبه. فالماكرو استبدال نصّي أعمى بلا نوع ولا احترام للنطاق، ولذلك لا يمكن فحصه في المنقّح ويُنتج رسائل خطأ مربكة. أما المتغير const (أو constexpr) فيخضع لفحص النوع وله نطاق مثل أي شيء آخر.

const مقابل constexpr

تمنحك كلتا الكلمتين المفتاحيتين قيمة لا يمكن أن تتغير، لكنهما تجيبان على سؤالين مختلفين. تقول const: «هذه لا تتغير أبدًا بعد ضبطها». وتقول constexpr ما هو أقوى: «هذه يمكن حسابها في وقت الترجمة» — وكل ما هو constexpr يكون تلقائيًا const أيضًا.

القاعدة العملية: لجأ إلى constexpr كلما كانت القيمة قيمة حرفية ثابتة أو حسابًا يستطيع المترجم إجراءه (أحجام المصفوفات، أطوال المخازن المؤقتة، تسميات switch، وسائط القوالب). واستخدم const المجرّدة عندما تُحدَّد القيمة في وقت التشغيل لكن يجب ألّا تتغير بعد ذلك — مثل نسخة const من وسيط دالة.

منذ C++20 توجد أيضًا consteval، وتُستخدم على الدوال التي يجب أن تُنفَّذ في وقت الترجمة:

consteval int square(int x) { return x * x; }
constexpr int area = square(8); // محسوبة أثناء الترجمة

دالة constexpr قد تُنفَّذ في وقت الترجمة؛ أما دالة consteval فيجب دائمًا أن تفعل ذلك، وإلا فهو خطأ.

المؤشرات و const: اقرأ بترتيب عكسي للقراءة

هنا تُربك const الناس، لأن الكلمة المفتاحية قد تقع على أي من جانبي *، والمعنيان متعاكسان. والحيلة هي قراءة التصريح بترتيب عكسي لاتجاه القراءة المعتاد.

اقرأ int* const p2 بترتيب عكسي: «p2 مؤشر ثابت إلى int». واقرأ const int* p1 على أنها: «p1 مؤشر إلى const int». إن أخطأت هنا، ستضيّع وقتًا حقيقيًا في الحيرة من خطأ يقول إنك لا تستطيع تعديل شيء ظننته قابلًا للتغيير.

مزلق عملي: لا تأخذ أبدًا عنوان كائن const ثم تزيل const عبر التحويل (cast) لتعديل الكائن الأساسي. فعل ذلك سلوك غير معرّف إذا كان الكائن الأصلي ثابتًا حقًا، وللمترجم حرية افتراض أن القيمة لا تتغير أبدًا — وقد تُتجاهَل «كتابتك» ببساطة.

مراجع const كمعاملات للدوال

أكثر استخدام يومي شيوعًا لـ const هو تمرير الكائنات الكبيرة بالمرجع دون نسخها. المعامل const& يتجنّب النسخ ويعد في الوقت نفسه بأن الدالة لن تعدّل وسيط المستدعي.

التمرير بـ const& هو الخيار الافتراضي لأي معامل أكبر من بضعة بايتات (السلاسل النصية، والمتجهات، وأصنافك الخاصة). لاحظ أنه يتيح للدالة أيضًا قبول قيمة مؤقتة مثل "Grace" — فـالمرجع غير الثابت العادي لا يمكنه الارتباط بقيمة مؤقتة، لذا فإن حذف const هنا سيرفض ذلك الاستدعاء الثاني.

دوال الأعضاء الثابتة

عند كتابة صنف، ضع const لاحقة على أي دالة (method) لا تعدّل الكائن. هذا ما يجعل الدالة قابلة للاستدعاء على نسخ const وعلى معاملات const& — فبدونه لا تستطيع حتى قراءة كائنك الخاص عبر مقبض const.

يُسمّى انضباط وسم الدوال للقراءة فقط بـ const بمصطلح const correctness. أتقِنه مبكرًا — فإضافة دالة كان ينبغي أن تكون const أمر سهل، لكن تطعيم const لاحقًا عبر قاعدة شيفرة كبيرة أمر مؤلم، لأن كل مستدعٍ يمرّ عبر مرجع const يعتمد عليه.

التالي: المعاملات (Operators)

الآن وقد صار بإمكانك تثبيت قيمك بـ const، فالخطوة التالية هي فعل أشياء بها. تغطّي صفحة المعاملات المعاملات الحسابية والمقارنة والمنطقية والإسناد — بما في ذلك مزالق القسمة الصحيحة، وأسبقية المعاملات، وكيفية تفاعل const مع معاملات الإسناد التي لا يُسمح لك باستخدامها.

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

ما الفرق بين const و constexpr في C++؟

تعني const أن القيمة لا يمكن أن تتغير بعد التهيئة، لكن يمكن حسابها أثناء التشغيل. أما constexpr فهي أقوى: تضمن أن القيمة يمكن حسابها في وقت الترجمة، لذا يمكن استخدامها حيثما يُطلب ثابت وقت ترجمة (أحجام المصفوفات، وسائط القوالب، تسميات switch). كل كائن constexpr هو أيضًا const، لكن ليس كل كائن const هو constexpr.

كيف أصرّح عن ثابت في C++؟

ضع const قبل النوع وأعطه قيمة: const int maxUsers = 100;. يجب تهيئة المتغير const عند التصريح عنه، لأنك لن تتمكن أبدًا من إسناد قيمة له لاحقًا. لثوابت وقت الترجمة، يُفضَّل constexpr int maxUsers = 100;. تجنّب ماكرو #define القديم على طراز C؛ فهو بلا نوع ويتجاهل النطاق.

ماذا يعني المؤشر الثابت في C++؟

يعتمد ذلك على موضع const. الصيغة const int* p هي مؤشر إلى ثابت؛ يمكنك إعادة توجيه p، لكن لا يمكنك تغيير *p. والصيغة int* const p هي مؤشر ثابت؛ يمكنك تغيير *p، لكن لا يمكنك إعادة توجيه p. اقرأ التصريح بترتيب عكسي للقراءة: int* const تعني «مؤشر ثابت إلى int».

Coddy programming languages illustration

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

ابدأ الآن