سؤال

العديد من اللغات، مثل Java، C #، لا تنفصل الإعلان عن التنفيذ. C # لديه مفهوم الطبقة الجزئية، ولكن لا يزال التنفيذ والإعلان في نفس الملف.

لماذا لا يكون C ++ نفس النموذج؟ هل هو أكثر عملية أن يكون لها ملفات الرأس؟

أنا أشير إلى الإصدارات الحالية والقادمة من معيار C ++.

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

المحلول

أنا تقلصت بشكل روتيني بين C # و C ++، ونقص ملفات الرأس في C # هي واحدة من أكبر peeves الحيوانات الأليفة. يمكنني أن ننظر إلى ملف رأس وتعلم كل ما أحتاج إلى معرفته حول الفصل - ما يسمى وظائف الأعضاء فيه، بناء جملة الاتصال، إلخ - دون الحاجة إلى Wade من خلال صفحات التعليمات البرمجية التي تنفذ الفصل.

ونعم، أعرف عن الطبقات الجزئية والجلوس، لكنها ليست هي نفسها. الفصول الجزئية تجعل المشكلة فعليا أسوأ، لأن تعريف الفصل ينتشر عبر العديد من الملفات. بقدر ما يذهب #regions، لا يبدو أنهم لا يتسعون أبدا بالطريقة التي أرغب في ما أقوم به في الوقت الحالي، لذلك يجب أن أقضي الوقت في توسيع تلك الزائد الصغير حتى أحصل على أيمن.

ربما إذا عمل Intellisense Visual Studio بشكل أفضل بالنسبة ل C ++، فلن أحصل على سبب مقنع للإشارة إلى ملفات .h في كثير من الأحيان، ولكن حتى في VS2008، C ++ Intellisense لا يمكن لمس C #

نصائح أخرى

التوافق الوراء - لا يتم القضاء على ملفات الرأس لأنه سيقتحم التوافق الخلفي.

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

هذا يسمح أيضا بإجراء SDKS أسهل قليلا. يمكنك تقديم الرؤوس فقط وبعض المكتبات. هناك، بالطبع، طرق حول هذا الاستخدام اللغات الأخرى.

حتى في بيارن ستروستروب وقد دعا رأس الملفات Kludge.

ولكن بدون تنسيق ثنائي قياسي يتضمن البيانات الوصفية الضرورية (مثل ملفات فئة Java، أو ملفات .NET PE) لا أرى أي طريقة لتنفيذ الميزة. قزم جردت أو A.Out Binary ليس لديه الكثير من المعلومات التي تحتاجها لاستخراجها. ولا أعتقد أن المعلومات يتم تخزينها من أي وقت مضى في ملفات Windows XCOFF.

في تصميم وتطور C ++, ، ستروستروب يعطي سبب آخر آخر ...

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

قد يبدو هذا غريب في هذه الأيام، لكنني أعتقد أنه كان مشكلة مهمة عندما تم اختراع C ++.

تم إجراء C لجعل كتابة مترجم بسهولة. إنه يفعل الكثير من الأشياء بناء على هذا المبدأ الوحيد. توجد مؤشرات فقط لجعل كتابة مترجم أسهل، كما تفعل ملفات الرأس. تستند العديد من الأشياء التي تم نقلها إلى C ++ إلى التوافق مع هذه الميزات المنفذة لجعل كتابة التحويل البرمجي أسهل.

إنها فكرة جيدة في الواقع. عندما تم إنشاء C، كانت ج و Unix نوعا من الزوج. ج يستند إلى جيم يونيكس، وتم تشغيل يونيكس في هذه الطريقة، سيتم الانتشار C و UNIX بسرعة من النظام الأساسي إلى المنصة في حين أن نظام تشغيل يعتمد على التجميع يجب إعادة كتابة تماما ليتم نقله بالكامل.

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

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

ج هي لغة رائعة وبسيطة. كان لديها مجموعة ميزة محدودة للغاية، ويمكنك كتابة مترجم دون الكثير من الجهد. تنقله بشكل عام! أنا لا أحاول أن أقول إنها لغة سيئة أو أي شيء، فهي فقط أهداف C. عند إنشاءها قد تغادر بقايا اللغة باللغة التي لا لزوم لها الآن، ولكن ستظل محيطا للحصول على التوافق.


يبدو أن بعض الناس لا يعتقدون حقا أن ج قد كتب إلى ميناء يونيكس، لذلك هنا:من)

تمت كتابة النسخة الأولى من UNIX في لغة المجمع، لكن نية طومسون هي أنها ستكون مكتوبة بلغة رفيعة المستوى.

حاول طومسون لأول مرة في عام 1971 لاستخدام Fortran على PDP-7، لكنه استسلم بعد اليوم الأول. ثم كتب لغة بسيطة جدا يسمى ب، الذي حصل على PDP-7. عملت، ولكن كانت هناك مشاكل. أولا، لأن التنفيذ قد تم تفسيره، كان دائما بطيئا. ثانيا، المفاهيم الأساسية ل B، التي كانت تستند إلى BCPL الموجهة إلى الكلمة، لم تكن مباشرة لآلة موجهة نحو البايت مثل PDP-11 الجديد.

استخدم Ritchie PDP-11 لإضافة أنواع إلى B، والتي كانت مدتها لفترة من الوقت NB ل "جديد ب"، ثم بدأ في كتابة مترجم لذلك. وقال ريتشي: "بحيث كانت المرحلة الأولى من C هذان المرحلتين حقا في سلسلة متتالية قصيرة، أولا، بعض التغييرات اللغوية من B، في الحقيقة، إضافة هيكل النوع دون تغيير كبير في بناء الجملة؛ والقيام بالمترجم".

وقال "المرحلة الثانية أبطأ"، وقال إن "المرحلة الثانية أبطأ". اخر؛ والصعوبة في الحصول على بنية البيانات المناسبة، لأن النسخة الأصلية من C لم يكن لها هياكل.

وقال ريتشي "مزيج من الأشياء تسبب كين للتخلي عن الصيف". "على مدار العام، أضفت هياكل وربما جعلت كود المترجم أفضل إلى حد ما - كود أفضل - وهكذا خلال الصيف المقبل، كان ذلك عندما حققنا الجهد المتضافر وفعلت بالفعل نظام التشغيل بأكمله في C."


إليك مثال مثالي على ما أقصده. من التعليقات:

توجد مؤشرات فقط لجعل كتابة مترجم أسهل؟ رقم المؤشرات موجودة لأنها أبسط تجريد ممكن حول فكرة عدم الإنذار. - آدم روزنفيلد (منذ ساعة)

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

المشكلة؟ لتنفيذ المصفوفات كفاءة كمؤشرات يجب أن تضيف إلى حد كبير كومة ضخمة من التعليمات البرمجية إلى برنامج التحويل البرمجي الخاص بك.

لا يوجد سبب لم يكن بإمكانهم تصميم ج دون مؤشرات، ولكن مع رمز مثل هذا:

int i=0;
while(src[++i])
    dest[i]=src[i];

سيستغرق الأمر الكثير من الجهد (على جزء المترجمين) للعاملين في إضافات I + SRC واضحة وأنا + Dests إضافية وجعلها تخلق نفس التعليمات البرمجية التي ستجعلها:

while(*(dest++) = *(src++))
    ;

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

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

إذا كنت تريد C ++ بدون ملفات رأس، فأنا أخبار جيدة بالنسبة لك.

إنه موجود بالفعل ويسمى D (http://www.digitalmars.com/d/index.html.)

يبدو أن D من الناحية الفنية أجمل كثيرا من C ++ ولكن ليس فقط السائدة بما يكفي للاستخدام في العديد من التطبيقات في الوقت الحالي.

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

كما تم تصميم C ++ الآن، فأنت بحاجة إلى النماذج الأولية - كما هو الحال في C - إلى التحقق من أي رمز مترجم يشير إلى الوظائف والفئات الخارجية. بدون ملفات الرأس، يجب عليك كتابة هذه التعريفات الفئة وإعلانات الوظائف قبل استخدامها. ل C ++ غير لاستخدام ملفات الرأس، سيتعين عليك إضافة ميزة باللغة التي من شأنها أن تدعم شيئا مثل Java import الكلمة الرئيسية. سيكون ذلك إضافة رئيسية، وتغيير؛ للإجابة على سؤالك إذا كان الأمر عمليا: لا أعتقد ذلك - ليس على الإطلاق.

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

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

هناك قيمة في تحديد الواجهة الفصل في مكون منفصل إلى ملف التنفيذ.

يمكن القيام به مع واجهات، ولكن إذا نزلت هذا الطريق، فأنت تقول ضمنا أن الفصول هي ناقصة من حيث فصل التنفيذ من العقد.

كان لدى Modula 2 الفكرة الصحيحة وحجز التعريف وحدات التنفيذ. http://www.modula2.org/Reference/modules.php.

الجواب Java / C # هو تطبيق ضمني لنفسه (على الرغم من وجوه المنحى.)

ملفات الرأس هي kludge، لأن ملفات الرأس التعبير عن تفاصيل التطبيق (مثل المتغيرات الخاصة).

عند الانتقال إلى Java و C #، أجد أنه إذا كانت اللغة تتطلب دعم IDE للتطوير (بحيث تكون واجهات الطبقة العامة قابلة للملاحة في متصفحات الفئة)، فربما يكون هذا عبارة أن الكود لا يقف على مزاياه الخاصة كونها قابلة للقراءة بشكل خاص.

أجد مزيج من الواجهة مع تفاصيل التنفيذ مروعة للغاية.

من الأهم من ذلك، يشير عدم وجود القدرة على توثيق توقيع الطبقة العامة في ملف موجز جيدا مستقلة عن التنفيذ إلى أن تصميم اللغة مكتوب لراحة التأليف، وهو راحة الصيانة بدلا من ذلك. حسنا أنا متجول حول جافا و C # الآن.

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

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

سيكون من الأسهل بكثير تغيير بيان التبديل إلى شيء ما في منتصف الطريق الذكي. من شأنه أن يكسر فقط رمز القليل. ما زال لن يحدث ذلك.

تم تحريره للحصول على فكرة جديدة:

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

لا توجد لغة بدون ملفات الرأس. انها أسطورة.

إلقاء نظرة على أي توزيع مكتبة خاصة ل Java (ليس لدي تجربة ج # للتحدث عنها، لكنني أتوقع أنها هي نفسها). أنها لا تعطيك ملف المصدر الكامل؛ أنها تعطيك فقط ملف مع التنفيذ كل طريقة مفرطة ({} أو {return null;} أو ما شابه) وكل ما يمكنهم الابتعاد مع إخفاء مخفي. لا يمكنك الاتصال بهذا أي شيء سوى رأسه.

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

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

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

mysource.h:

extern int my_library_entry_point(int api_to_use, ...);

mysource.c:

int private_function_that_CANNOT_be_public();

int my_library_entry_point(int api_to_use, ...){
  // [...] Do stuff
}

int private_function_that_CANNOT_be_public() {

}

اذا أنت #include <MySource.h>, ، ثم تحصل my_library_entry_point.

اذا أنت #include <MySource.c>, ، ثم أنت أيضا احصل على private_function_that_CANNOT_be_public.

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

نعم بالتأكيد!

بعد الترميز في Java و C # من مزعج حقا أن يكون لديك ملفين لكل فصول. لذلك كنت أفكر كيف يمكنني دمجها دون كسر الرمز الحالي.

في الواقع، إنه سهل حقا. فقط ضع التعريف (التنفيذ) داخل قسم #IFDEF وإضافة تعريف في سطر أوامر المحول البرمجي لتجميع هذا الملف. هذا هو.

هنا مثال:

/* File ClassA.cpp */

#ifndef _ClassA_
#define _ClassA_

#include "ClassB.cpp"
#include "InterfaceC.cpp"

class ClassA : public InterfaceC
{
public:
    ClassA(void);
    virtual ~ClassA(void);

    virtual void methodC();

private:
    ClassB b;
};

#endif

#ifdef compiling_ClassA

ClassA::ClassA(void)
{
}

ClassA::~ClassA(void)
{
}

void ClassA::methodC()
{
}

#endif

في سطر الأوامر، ترجمة هذا الملف

-D compiling_ClassA

الملفات الأخرى التي تحتاج إلى تضمين Classa يمكن أن تفعل فقط

#include "ClassA.cpp"

بالطبع يمكن بسهولة إضافة إضافة تعريف في سطر الأوامر بسهولة مع توسيع ماكرو (مترجم Visual Studio) أو بمتغيرات تلقائية (GNU Make) واستخدام نفس التسميات لاسم تحديد الاسم.

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

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