سؤال

في لغة C++، يمكن فصل تعريف وتعريف الوظائف والمتغيرات والثوابت كما يلي:

function someFunc();

function someFunc()
{
  //Implementation.
}

في الواقع، في تعريف الطبقات، هذا هو الحال غالبًا.عادةً ما يتم الإعلان عن الفصل مع أعضائه في ملف ‎.h، ثم يتم تعريفهم في ملف ‎.C المطابق.

ما هي مزايا وعيوب هذا النهج؟

هل كانت مفيدة؟

المحلول

وتاريخيا كان هذا لمساعدة مترجم. كان عليك أن تعطيه قائمة أسماء قبل أن استخدمها - سواء كان هذا الاستخدام الفعلي، أو إعلان إلى الأمام (C افتراضي funcion النموذج جانبا)

وتبين المجمعين الحديثة للغات الحديثة أن هذا لم يعد أمرا ضروريا، لذلك C & C ++ الصورة (وكذلك الهدف-C، وربما غيرها) في بناء الجملة هنا الأمتعة histotical. في الواقع واحدة هذه هي واحدة من المشاكل الكبيرة مع C ++ أنه حتى إضافة نظام وحدة السليم لن تحل.

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

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

نصائح أخرى

إنها قطعة أثرية لكيفية عمل برامج التحويل البرمجي C/C++.

عندما يتم تجميع الملف المصدر، يقوم المعالج المسبق باستبدال كل #include-statement بمحتويات الملف المضمن.بعد ذلك فقط يحاول المترجم تفسير نتيجة هذا التسلسل.

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

هناك مشكلة في ذلك، عندما يتعلق الأمر باستدعاءات الوظائف المتكررة بشكل متبادل:

void foo()
{
  bar();
}

void bar()
{
  foo();
}

هنا، foo لن يتم تجميعها كـ bar غير معروف.إذا قمت بتبديل الوظيفتين، bar لن يتم تجميعها كـ foo غير معروف.

إذا قمت بفصل الإعلان والتعريف، فيمكنك ترتيب الوظائف كما يحلو لك:

void foo();
void bar();

void foo()
{
  bar();
}

void bar()
{
  foo();
}

هنا، عندما عمليات المترجم foo فهو يعرف بالفعل توقيع دالة تسمى bar, ، وهو سعيد.

بالطبع يمكن للمترجمين العمل بطريقة مختلفة، ولكن هذه هي الطريقة التي يعملون بها في C وC++ وإلى حد ما Objective-C.

سلبيات:

لا شيء مباشرة.إذا كنت تستخدم C/C++ على أي حال، فهي أفضل طريقة للقيام بالأشياء.إذا كان لديك خيار اللغة/المترجم، فربما يمكنك اختيار واحد حيث لا يمثل هذا مشكلة.الشيء الوحيد الذي يجب مراعاته عند تقسيم الإعلانات إلى ملفات رأس هو تجنب عبارات #include-التكرارية المتبادلة - ولكن هذا هو ما تهدف إليه عمليات التضمين.

مزايا:

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

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

وهناك نوعان من المزايا الرئيسية لفصل الإعلان والتعريف إلى ملفات رأس ومصدر C ++. الأول هو أن تتجنب المشاكل مع القاعدة واحدة تعريف عندما صفك / وظائف / كل ما هم #included في أكثر من مكان واحد. ثانيا، عن طريق القيام بأشياء بهذه الطريقة، يمكنك فصل واجهة والتنفيذ. مستخدمي صفك أو مكتبة تحتاج فقط لرؤية ملف الرأس الخاص بك من أجل كتابة التعليمات البرمجية التي تستخدمها. يمكنك أيضا أن تأخذ هذه خطوة واحدة أبعد مع Pimpl المصطلح و تجعل من ذلك أن المستخدم لم يقم الرمز إلى إعادة ترجمة في كل مرة تتغير تنفيذ المكتبة.

ولقد سبق ذكره عيب تكرار كود بين الملفات. ح و.CPP. ربما قمت بكتابة كود C ++ لفترة طويلة جدا، ولكن لا أعتقد أنها <م> أن سيئا. لديك لتغيير كل رمز المستخدم في كل مرة قمت بتغيير توقيع ظيفة على أي حال، فما هو ملف أكثر واحد؟ إنه أمر مزعج فقط عندما كنت تكتب أولا فئة وكان لديك لنسخ ولصق من الرأس إلى ملف مصدر جديد.

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

وهذا المعيار يتطلب عند استخدام وظيفة، يجب أن يكون إعلانا في نطاقها. وهذا يعني، أن المترجم يجب أن تكون قادرة على التحقق ضد نموذج (إعلان في ملف الرأس) ما تمر به. ما عدا بالطبع، لوظائف التي هي variadic - هذه الوظائف لا صحة الحجج

.

وفكر C، عندما يكون هذا لم يكن مطلوبا. في ذلك الوقت، عالجت المجمعين أي مواصفات نوع الإرجاع إلى أن تعثر لكثافة العمليات. الآن، افترض كان لديك فو وظيفة () الذي عاد مؤشر إلى باطل. ومع ذلك، منذ كنت لا تملك هذا الإعلان، فإن المترجم يعتقدون أن لديها للعودة عدد صحيح. على بعض الأنظمة موتورولا على سبيل المثال، يمكن أن تعاد integeres والمؤشرات في سجلات مختلفة. الآن، فإن المترجم لم يعد استخدام السجل الصحيح وبدلا من العودة يلقي المؤشر إلى عدد صحيح في السجل الآخر. لحظة محاولة للعمل مع هذا المؤشر - ينهار كل شيء

.

وإعلان وظائف داخل الرأس على ما يرام. ولكن تذكر إذا قمت بتعريف وتحديد في رأس التأكد من أنها مضمنة. طريقة واحدة لتحقيق ذلك هو وضع تعريف داخل تعريف الفئة. إلا إلحاقها الكلمة inline. واجهت انتهاك ODR إلا عندما يتم تضمين رأس في ملفات تنفيذ متعددة.

وأساسا لديك 2 وجهات النظر حول الطبقة / وظيفة / أيا كان:

والإعلان، حيث تقوم بتعريف اسم والمعلمات وأعضاء (في حالة وجود البنية / فئة)، وتحديد المكان الذي تحدد ما هي وظائف لا.

ومن بين العيوب هي التكرار، ولكن ميزة واحدة كبيرة هي أنك يمكن أن تعلن وظيفة الخاص بك كما int foo(float f) وترك التفاصيل في التنفيذ (= التعريف)، لذلك أي شخص يريد استخدام وظيفة فو الخاص بك يتضمن فقط ملف الرأس والروابط إلى مكتبة / objectfile، مستخدمي المكتبات ذلك فضلا عن المجمعين فقط لرعاية واجهة محددة، مما يساعد على فهم واجهات وبسرعة تصل تجميع مرات.

ميزة واحدة لم أرها بعد: واجهة برمجة التطبيقات

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

تنصل:أنا لا أقول ما إذا كان ذلك صحيحا أو خطأ أو مبررا، أنا فقط أقول أنني رأيت ذلك كثيرا.

ميزة

يمكن الرجوع إلى الفئات من ملفات أخرى بمجرد تضمين الإعلان.يمكن بعد ذلك ربط التعريفات لاحقًا في عملية التجميع.

وميزة واحدة كبيرة من الإعلانات إلى الأمام هو أنه عندما تستخدم بعناية يمكنك خفض تبعيات وقت الترجمة بين الوحدات.

إذا يحتاج ClassA.h للإشارة إلى عناصر البيانات في ClassB.h، يمكنك غالبا ما تستخدم مجرد إشارات إلى الأمام في ClassA.h وتشمل ClassB.h في ClassA.cc بدلا من ClassA.h، وبالتالي خفض لتجميع الوقت التبعية.

لأنظمة كبيرة يمكن أن يكون هذا الوقت المدخر ضخمة على البناء.

  1. يوفر الفصل رؤية نظيفة ومرتبة لعناصر البرنامج.
  2. إمكانية إنشاء وحدات/مكتبات ثنائية والارتباط بها دون الكشف عن المصادر.
  3. ربط الثنائيات دون إعادة ترجمة المصادر.

عند القيام به بشكل صحيح، ويقلل هذا الفصل تجميع الأوقات التي تغيرت فقط تنفيذ.

عيب

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top