سؤال

لقد تم برمجة لبضع سنوات حتى الآن و قد استخدمت مؤشرات دالة في بعض الحالات.ما أود معرفته هو متى يكون من المناسب أو عدم استخدامها لأسباب تتعلق بالأداء و أعني في سياق الألعاب, لا البرمجيات التجارية.

مؤشرات الدالة سريعة جون كارماك تستخدم إلى حد الاعتداء في Quake و Doom شفرة المصدر لأنه عبقري :)

وأود أن استخدام مؤشرات الدالة المزيد ولكن أريد أن استخدامها حيث أنها هي الأنسب.

في هذه الأيام ما هي أفضل الاستخدامات العملية من مؤشرات الدالة في الحديث ج-أسلوب لغات مثل C, C++, C# و Java ، وما إلى ذلك ؟

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

المحلول

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

مؤشرات دالة لها علاقة مع الأداء ، ينبغي أبدا أن تستخدم للحصول على الأداء.

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

مثال بسيط هو عام وظيفة الفرز.يجب أن يكون لديك بعض طريقة لمقارنة اثنين من العناصر من أجل تحديد الكيفية التي ينبغي أن تكون مرتبة.هذا يمكن أن يكون مؤشر دالة تمريرها إلى وظيفة الفرز و في الواقع c++'s std::النوع() يمكن استخدامها تماما مثل ذلك.إذا كنت تسأل إلى نوع متواليات من النوع الذي لا يعرف أقل من المشغل ، عليك أن تمر في مؤشر دالة يمكن أن الدعوة لإجراء المقارنة.

و هذا يقودنا بشكل جيد إلى بديل متفوقة.في C++, أنت لا تقتصر على مؤشرات الدالة.كنت غالبا ما تستخدم functors بدلا من ذلك - وهذا هو ، الفئات التي تفرط في المشغل () ، حتى أنها يمكن أن تكون "يسمى" كما لو كانوا وظائف.Functors يكون الزوجان من مزايا كبيرة على مؤشرات الدالة:

  • وأنها توفر المزيد من المرونة:إنهم كاملة الطبقات ، مع منشئ ، المدمر الأعضاء المتغيرات.يمكن الحفاظ على الدولة ، وقد تعرض أعضاء أخرى وظائف المحيطة رمز يمكن استدعاء.
  • فهي أسرع:على عكس مؤشرات الدالة التي اكتب فقط ترميز التوقيع على وظيفة (متغير من نوع void (*)(int) قد يكون أي الذي يأخذ الباحث وإرجاع الفراغ.لا يمكننا أن نعرف أي واحد) ، functor نوع تشفير الدقيق الوظيفة التي ينبغي أن تسمى (منذ functor هي فئة نسميها ج ، ونحن نعلم أن وظيفة الدعوة ، وسوف يكون دائما ، ج::المشغل()).و هذا يعني المترجم أن مضمنة استدعاء دالة.هذا هو السحر الذي يجعل عامة std::فرز سريع مثل يدك تلوينها وظيفة الفرز مصممة خصيصا نوع البيانات الخاص بك.المترجم يمكن القضاء على جميع النفقات العامة من استدعاء دالة معرفة من قبل المستخدم.
  • فهي أكثر أمانا:هناك القليل جدا من نوع السلامة في مؤشر دالة.لا يوجد لديك ضمان أنه يشير إلى صالح العمل.يمكن أن تكون فارغة.و معظم المشاكل مع المؤشرات تنطبق على مؤشرات الدالة أيضا.إنهم خطورة وعرضة للخطأ.

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

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

لذلك هذا هو حيث يمكنك استخدام مؤشرات الدالة:عندما أكثر تطورا لا تتوافر بدائل (ie.كنت عالقة في ج) تحتاج إلى تمرير وظيفة إلى أخرى.السيناريو الأكثر شيوعا هو رد.يمكنك تعريف الدالة F التي تريد نظام الاتصال عند X يحدث.لذلك قمت بإنشاء وظيفة مؤشر لافتا إلى واو ، و تمرير هذا النظام في السؤال.

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

نصائح أخرى

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

int MyFunc()
{
  if(SomeFunctionalityCheck())
  {
    ...
  }
  else
  {
    ...
  }
}

إذا تم استدعاء هذه الدالة داخل العميق من الحلقات الهامة ثم على نحو أفضل على الأرجح إلى استخدام مؤشر دالة لMyFunc:

int (*MyFunc)() = MyFunc_Default;

int MyFunc_SomeFunctionality()
{
  // if(SomeFunctionalityCheck())
  ..
}

int MyFunc_Default()
{
  // else
  ...
}

int MyFuncInit()
{
  if(SomeFunctionalityCheck()) MyFunc = MyFunc_SomeFunctionality;
}

وهناك استخدامات أخرى بالطبع، مثل ظائف رد ، تنفيذ التعليمات البرمجية بايت من الذاكرة أو لخلق لغة تفسيرها.

لتنفيذ إنتل متوافقة رمز بايت على ويندوز، والتي قد تكون مفيدة ل مترجم. على سبيل المثال، وهنا هو وظيفة STDCALL عودة 42 (0x2A) المخزنة في صفيف الذي يمكن تنفيذه:

code = static_cast<unsigned char*>(VirtualAlloc(0, 6, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
// mov eax, 42
code[0] = 0x8b;
code[1] = 0x2a;
code[2] = 0x00;
code[3] = 0x00;
code[4] = 0x00;
// ret
code[5] = 0xc3;
// this line executes the code in the byte array
reinterpret_cast<unsigned int (_stdcall *)()>(code)();

...

VirtualFree(code, 6, MEM_RELEASE);

و)؛

وأي وقت كنت تستخدم معالج الحدث أو مندوب في C #، كنت تستخدم بفعالية مؤشر دالة.

ولا، فهي ليست حول السرعة. مؤشرات الدالة على وشك الراحة.

وجوناثان

وهذه الأيام ما هي أفضل والأكثر عملية استخدامات الأعداد الصحيحة في اللغات الحديثة على الطراز ج؟

تستخدم

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

وقال ذلك، سوف توفر اقتباس حصلت عليها من أستاذ سابق لي:

<اقتباس فقرة>   

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

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

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

وبعد هذه العملية رسميا في اللغات OO الأفضل في كل شيء ذات معنى.

ويتحدث فقط من C #، ولكن يتم استخدام مؤشرات الدالة في جميع أنحاء C #. المندوبين ومناسبات (وLambdas، الخ) كلها مؤشرات الدالة تحت غطاء محرك السيارة، لذلك ما يقرب من أي # مشروع C ستكون مليئة مؤشرات الدالة. أساسا كل معالج الأحداث، بالقرب من كل استعلام LINQ، الخ - سوف تستخدم مؤشرات الدالة

وهناك مناسبات عند استخدام مؤشرات الدالة يمكن تسريع المعالجة. الجداول ارسال بسيطة يمكن أن تستخدم بدلا من الجمل التحويلية طويلة أو تسلسل إذا، ثم، شيء آخر.

وظيفة مؤشرات رجل فقير محاولة أن تكون وظيفية.حتى يمكن تقديم حجة أن وجود مؤشرات الدالة يجعل اللغة الوظيفية ، حيث يمكنك كتابة أعلى الوظائف معهم.

دون إغلاق جملة سهلة, أنهم شيء الإجمالي.إذا كنت تميل إلى استخدام لهم أقل بكثير من ومرغوب فيه.أساسا "رد" وظائف.

في بعض الأحيان, OO أعمال التصميم حول استخدام المهام بدلا خلق كل نوع واجهة لتمرير في الوظيفة المطلوبة.

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

تحرير أحد التعليقات قال يجب أن يكون هناك مظاهرة من أعلى الوظائف مع مؤشرات الدالة.أي وظيفة أخذ وظيفة رد الاتصال هو أعلى أجل وظيفة.مثل القول ، EnumWindows:

BOOL EnumWindows(          
    WNDENUMPROC lpEnumFunc,
    LPARAM lParam
);

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

على .NET framework هي أيضا مليئة تصاميم مماثلة.فعلى سبيل المثال ، IAsyncResult.AsyncState:"يحصل المستخدم-تعريف الكائن الذي يؤهل أو يحتوي على معلومات حول عملية غير متزامنة." منذ IAR هو كل ما تحصل على رد ، دون إغلاق ، أنت بحاجة إلى وسيلة دفع بعض البيانات في المتزامن op لذا أنت يمكن أن يلقي بها في وقت لاحق.

حسب رأيي الشخصي الخبرة يمكن أن تساعدك على توفير خطوط كبيرة من التعليمات البرمجية.

النظر في حالة:{

switch(sample_var)
{

case 0:
          func1(<parameters>);
          break;

case 1:
          func2(<parameters>);
          break;











up to case n:
                funcn(<parameters>);
                break;

}

حيث func1() ...funcn() هي من مهام مع نفسه الأولي.ما يمكننا فعله هو:تعلن مجموعة من مؤشرات الدالة arrFuncPoint التي تحتوي على عناوين الوظائف func1() إلى funcn()

ثم كل تبديل الحالة سيكون محلها

*arrFuncPoint[sample_var];

<اقتباس فقرة>   <اقتباس فقرة>     

والمؤشرات وظيفة سريعة

  

في أي سياق؟ مقارنة؟

وهذا يبدو وكأنه كنت ترغب فقط في استخدام مؤشرات الدالة من أجل استخدامها. ومن شأن ذلك أن يكون سيئا.

وعادة ما يستخدم مؤشر دالة بمثابة رد أو معالج الحدث.

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