إعلان مسبق

في برمجة الحاسوب ، الإعلان المسبق هو إعلان عن مُعرّف (يشير إلى كيان مثل نوع أو متغير أو ثابت أو دالة) لم يقدم المبرمج تعريفًا كاملاً له بعد .

يحتاج المُصرّف إلى معرفة بعض خصائص المُعرّف (مثل حجمه لتخصيص الذاكرة ، ونوع بياناته للتحقق من النوع ، كتوقيع نوع الدوال)، ولكنه ليس مُلزماً بمعرفة تفاصيل أخرى، كالقيمة التي يحملها (في حالة المتغيرات أو الثوابت) أو تعريفه (في حالة الدوال). يُعدّ هذا مفيداً بشكل خاص للمُصرّفات أحادية المرور والترجمة المنفصلة .

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

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

أمثلة

مثال أساسي في لغة C هو:

void printThisInteger ( int );

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

void printThisInteger ( int x ) { printf ( "%d \n " , x ); }

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

int foo ; // قد يكون foo مُعرّفًا في مكان ما في هذا الملف extern int bar ; // يجب تعريف bar في ملف آخر

في لغة باسكال وغيرها من لغات البرمجة التي تعتمد على خوارزمية ويرث ، من القواعد العامة ضرورة تعريف جميع الكيانات قبل استخدامها، وبالتالي فإن التعريف المسبق ضروري للتكرار المتبادل، على سبيل المثال. في لغة سي، تنطبق القاعدة العامة نفسها، ولكن باستثناء الدوال غير المُعرَّفة والأنواع غير المكتملة. لذا، في لغة سي، من الممكن (وإن كان غير مستحسن) تنفيذ زوج من الدوال المتكررة المتبادلة على النحو التالي:

int first ( int x ) { if ( x == 0 ) return 1 ; else return second ( x -1 ); // مرجع إلى second }int second ( int x ) { if ( x == 0 ) return 0 ; else return first ( x -1 ); // مرجع عكسي إلى first }

في لغة باسكال، يتطلب نفس التنفيذ تعريفًا مسبقًا للمعرّف secondقبل استخدامه في الدالة first. وبدون هذا التعريف المسبق، سيُصدر المُصرّف رسالة خطأ تُشير إلى أنه تم استخدام المعرّفsecond دون تعريفه.

الصفوف الدراسية

في بعض لغات البرمجة الكائنية مثل C++ و Objective-C ، يكون من الضروري أحيانًا تعريف الفئات مسبقًا. ويتم ذلك في الحالات التي يكون فيها من الضروري معرفة أن اسم الفئة يمثل نوعًا، ولكن لا داعي لمعرفة بنيتها.

في لغة C++، يمكن تعريف الفئات والهياكل مسبقًا على النحو التالي:

class MyClass ; struct MyStruct ;

في لغة C++، يُمكن تعريف الأصناف مسبقًا إذا كنتَ تحتاج فقط إلى استخدام نوع المؤشر إلى ذلك الصنف (بما أن جميع مؤشرات الكائنات لها نفس الحجم، وهذا ما يهم المُصرّف). يُعدّ هذا مفيدًا بشكل خاص داخل تعريفات الأصناف، على سبيل المثال إذا كان الصنف يحتوي على عضو يُمثّل مؤشرًا (أو مرجعًا) إلى صنف آخر.

يُستخدم التصريح المسبق لتجنب الترابط غير الضروري، مما يُساعد على تقليل وقت الترجمة بتقليل عدد ملفات الرأس المُضمنة. وهذا يُحقق ثلاث مزايا:

  • تقليل عدد الملفات التي يتم فتحها بواسطة #include (وبالتالي تقليل عدد استدعاءات نظام التشغيل )
  • تقليل حجم الملفات المعالجة مسبقًا (لأن رأس الملف غير مضمن)
  • تقليل تأثير إعادة التجميع عند تعديل الفئة المعلنة مسبقًا.

لا يكفي الإعلان المسبق عن فئة ما إذا كنت بحاجة إلى استخدام نوع الفئة الفعلي، على سبيل المثال، إذا كان لديك عضو نوعه هو تلك الفئة مباشرة (وليس مؤشرًا)، أو إذا كنت بحاجة إلى استخدامه كفئة أساسية، أو إذا كنت بحاجة إلى استخدام أساليب الفئة في أسلوب ما.

في لغة Objective-C، يمكن تعريف الفئات والبروتوكولات مسبقًا على النحو التالي:

@class MyClass ; @protocol MyProtocol ; 

في لغة Objective-C، يمكن تعريف الأصناف والبروتوكولات مسبقًا إذا كنت تحتاج فقط إلى استخدامها كجزء من نوع مؤشر كائن، مثل MyClass * أو id<MyProtocol> . يُعد هذا مفيدًا بشكل خاص داخل تعريفات الأصناف، على سبيل المثال إذا كان الصنف يحتوي على عضو يُشير إلى صنف آخر؛ لتجنب المراجع الدائرية (أي أن هذا الصنف قد يحتوي أيضًا على عضو يُشير إلى هذا الصنف)، نقوم ببساطة بتعريف الأصناف مسبقًا.

لا يكفي الإعلان المسبق عن فئة أو بروتوكول إذا كنت بحاجة إلى إنشاء فئة فرعية من تلك الفئة أو تنفيذ ذلك البروتوكول.

مرجع لاحق

يُستخدم مصطلح "المرجع الأمامي" أحيانًا كمرادف لمصطلح " التصريح الأمامي" . [ 1 ] ومع ذلك، يُفهم غالبًا على أنه يشير إلى الاستخدام الفعلي للكيان قبل أي تصريح؛ أي أن أول مرجع إلى secondفي الكود أعلاه هو مرجع أمامي. [ 2 ] [ 3 ] وبالتالي، يمكننا القول إنه نظرًا لأن التصريحات الأمامية إلزامية في لغة باسكال، فإن المراجع الأمامية ممنوعة.

مثال على الإشارة الأمامية (الصحيحة) في لغة C++ :

class C { public : void mutator ( int x ) { myValue = x ; } int accessor () const { return myValue ; } private : int myValue ; };

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

إن السماح بالمراجع الأمامية يمكن أن يزيد بشكل كبير من تعقيد ومتطلبات الذاكرة للمترجم، ويمنع عمومًا تنفيذ المترجم في خطوة واحدة .

مراجع

  1. MSDN: التحويل إلى نوع فئة مرجعية أمامية
  2. "المحاضرة 25" (ملف PDF) . مؤرشف من النسخة الأصلية (ملف PDF) بتاريخ 22-05-2011.
  3. "التفكير بلغة C++: العبارات المضمنة والمترجم" . مؤرشف من الأصل بتاريخ 2016-03-04 . تم الاطلاع عليه بتاريخ 2015-10-26 .