PHP الارتباط الثابت المتأخر. تعدد الأشكال في جاوة. الربط الديناميكي والثابت. تهيئة الكائنات. سلوك الطرق متعددة الأشكال عند استدعائها من المنشئين. الاختلافات بين الربط المبكر والمتأخر في Java

05.04.2024 برمجة

23 مارس 2010 dec5e

قدم PHP 5.3 ميزة مثيرة للاهتمام تسمى الربط الثابت المتأخر. ما يلي هو ترجمة مجانية قليلاً للوصف من الدليل الرسمي.

منذ PHP 5.3.0، قدمت اللغة ميزة تسمى الربط الثابت المتأخر، والتي يمكن استخدامها للإشارة إلى فئة قابلة للاستدعاء في سياق الميراث الثابت.

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

القيود الذاتية::

المثال رقم 1: استخدام الذات::

سيخرج المثال:

استخدام الربط الثابت المتأخر

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

المثال رقم 2: الاستخدام البسيط للثابت::

سيخرج المثال:

ملحوظة: static:: لا يعمل مثل $this للطرق الثابتة! $this-> يتبع قواعد الميراث، لكن static:: لا يتبع ذلك. ويتم توضيح هذا التمييز أدناه.

المثال رقم 3: استخدام ثابت:: في سياق غير ثابت

امتحان()؛ ?>

سيخرج المثال:

ملاحظة: يؤدي الربط الثابت المتأخر إلى إيقاف عملية حل المكالمة. تقوم المكالمات الثابتة باستخدام الكلمات الأساسية الأصلية:: أو الذاتية:: بتمرير معلومات المكالمة.

مثال رقم 4: تحويل المكالمات وعدم تحويلها

سيتم إخراج المثال

حالات الحافة

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

المثال رقم 5: الربط الثابت المتأخر في الطرق السحرية

فو؛ ?>

الارتباط الثابت باعتباره الأمثل

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

1 Fلا يتم تجاوزه في أي مكان في النظام (لديه إعلان واحد فقط)؛

2 سليست متعددة الأشكال، أي أنها ليست هدفًا لأي مرفق مصدره من نوع مختلف.

في أي من هذه الحالات، تم اكتشافه بواسطة مترجم جيد، وتم إنشاؤه لـ س.ف (أ، ب، ج...)قد يكون الكود هو نفس الكود الذي تم إنشاؤه بواسطة مترجمي C أو Pascal أو Ada أو Fortran للاتصال به و (خ، أ، ب، ج...). لن تكون هناك حاجة إلى تكاليف عامة.

يقوم مترجم ISE، وهو جزء من البيئة الموصوفة في المحاضرة الأخيرة، حاليًا بإجراء التحسين (1)، ومن المخطط إضافة (2) (تحليل (2) هو، في الواقع، نتيجة لتحليل النوع الآليات الموضحة في محاضرة الكتابة).

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

4. استخدم كلما أمكن ذلك الاستبدال التلقائي لرمز الإجراء.

يعني هذا الاستبدال توسيع نص البرنامج بنص الإجراء المطلوب في المكان الذي يتم استدعاؤه فيه. على سبيل المثال، لهذا الإجراء

set_a(x:SOME_TYPE) هو

اجعل x القيمة الجديدة للسمة أ.

يمكن للمترجم توليد للاتصال s.set_a(some_value)نفس الكود الذي سيقوم مترجم باسكال بإنشائه للمهمة s.a:= some_value(التسمية غير مقبولة لدينا لأنها تنتهك إخفاء المعلومات). في هذه الحالة لا يوجد أي حمل على الإطلاق، حيث أن التعليمات البرمجية التي تم إنشاؤها لا تحتوي على استدعاء الإجراء.

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

بالنسبة لكل استدعاء ينطبق عليه الارتباط الثابت التلقائي (1)، يمكن لمترجم OO أن يحدد، استنادًا إلى تحليل مقايضة ذاكرة الوقت، ما إذا كان استبدال رمز الإجراء التلقائي (3) جديرًا بالاهتمام. يعد هذا واحدًا من أكثر التحسينات المذهلة - وهو أحد الأسباب التي تجعل من الممكن تحقيق كفاءة كود C أو Fortran المنتج يدويًا، وفي بعض الأحيان، تجاوزه على الأنظمة الكبيرة.

إلى مكاسب الكفاءة التي تزداد مع حجم البرنامج وتعقيده، يضيف استبدال التعليمات البرمجية التلقائي فائدة زيادة الموثوقية والمرونة. كما هو مذكور، يكون استبدال التعليمات البرمجية صحيحًا من الناحية الدلالية فقط لإجراء يمكن تقييده بشكل ثابت، كما هو الحال في الحالتين (1) و(2). هذا ليس مقبولاً فحسب، بل يتوافق أيضًا تمامًا مع طريقة OO، ولا سيما مع مبدأ Open-Closed، إذا أضاف المطور، في منتصف الطريق خلال تطوير نظام كبير، تجاوزًا لبعض المكونات التي كان لها في تلك اللحظة مكون واحد فقط تطبيق. إذا تم إدراج رمز الإجراء يدويًا، فقد تكون النتيجة برنامجًا ذو دلالات خاطئة (نظرًا لأنه في هذه الحالة يكون الارتباط الديناميكي مطلوبًا، وإدراج الكود، بالطبع، يعني الارتباط الثابت). يجب أن يركز المطورون على بناء البرامج الصحيحة بدلاً من التحسينات المملة التي تؤدي إلى حدوث أخطاء عند إجرائها يدويًا، ولكنها في الواقع يمكن أن تكون آلية.

ملاحظة أخيرة حول الكفاءة. تشير الإحصائيات المنشورة للغات الموجهة للكائنات إلى أن ما بين 30% إلى 60% من المكالمات تستخدم فعليًا الربط الديناميكي. يعتمد هذا على مدى كثافة استخدام المطورين لخصائص محددة للطرق. وفي نظام ISE تقترب هذه النسبة من 60%. باستخدام التحسينات الموضحة للتو، فإنك تدفع فقط مقابل ربط المكالمات التي تحتاج إليها ديناميكيًا فقط. بالنسبة للمكالمات الديناميكية المتبقية، فإن الحمل ليس صغيرًا فقط (مقتصرًا على ثابت)، ولكنه ضروري أيضًا منطقيًا - في معظم الحالات، لتحقيق نتيجة مكافئة للربط الديناميكي، سيتعين عليك استخدام العبارات الشرطية ( اذا ثم...أو حالة من ...)، والتي قد تكون أكثر تكلفة من الآلية البسيطة القائمة على المصفوفة المذكورة أعلاه. لذلك ليس من المستغرب أن تتمكن برامج OO المجمعة باستخدام مترجم جيد من التنافس مع كود C المكتوب بخط اليد.

من كتاب تعزيز موقع الويب الخاص بك مؤلف ماتسيفسكي نيكولاي

الأرشفة الثابتة أثناء العمل هناك طريقة للقيام بذلك باستخدام سطرين فقط في ملف التكوين (httpd.conf أو .htaccess، الأول هو الأفضل)، إذا قضيت بضع دقائق وأرشفت جميع الملفات الضرورية بنفسك. لنفترض أن لدينا

من كتاب الدليل المرجعي لـ C++ مؤلف ستروستراب بيارن

R.3.3 البرنامج والربط يتكون البرنامج من ملف واحد أو أكثر مرتبطين ببعضهم البعض (§R.2). يتكون الملف من سلسلة من الأوصاف. اسم نطاق الملف الذي تم تعريفه بشكل صريح بأنه ثابت يكون محليًا لوحدة الترجمة الخاصة به ويمكنه

من كتاب لغة البرمجة C# 2005 ومنصة .NET 2.0. بواسطة ترويلسن أندرو

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

من كتاب أركيكاد 11 مؤلف دنيبروف ألكسندر ج

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

من كتاب أساسيات البرمجة الشيئية بواسطة ماير برتراند

الربط الديناميكي إن الجمع بين الآليتين الأخيرتين، التجاوز وتعدد الأشكال، يشير بشكل مباشر إلى الآلية التالية. لنفترض أن هناك مكالمة هدفها كيان متعدد الأشكال، على سبيل المثال، يستدعي كيان من النوع BOAT مكون الدوران.

من كتاب برمجة النظام في بيئة ويندوز بقلم هارت جونسون م

الارتباط بفئة ADT A، كما قيل عدة مرات، هو تطبيق لـ ADT، سواء تم تحديده رسميًا أو ضمنيًا. في بداية المحاضرة لوحظ أنه يمكن اعتبار العبارات وسيلة لإدخال الخصائص الدلالية التي تكمن في الصف.

من كتاب بنية TCP/IP والبروتوكولات والتنفيذ (بما في ذلك إصدار IP 6 وأمن IP) بقلم فيث سيدني م

الربط الديناميكي سوف يكمل الربط الديناميكي التجاوز وتعدد الأشكال والكتابة الثابتة، مما يؤدي إلى إنشاء رباعية أساسية

من كتاب VBA للدمى بواسطة ستيف كامينغز

زر باسم مختلف: عندما يكون الارتباط الثابت خاطئًا الآن، يجب أن تكون الوجبات الرئيسية من مبادئ الوراثة الموضحة في هذه المحاضرة واضحة: مبدأ الربط الديناميكي عندما لا تتطابق نتيجة الارتباط الثابت مع النتيجة

من كتاب نظام التشغيل UNIX مؤلف روباتشيفسكي أندريه م.

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

من كتاب C++ للمبتدئين بواسطة ليبمان ستانلي

الارتباط الضمني يعد الارتباط الضمني، أو الارتباط في وقت التحميل، أبسط أسلوبي الربط. إجراءات استخدام Microsoft C++ هي كما يلي: 1. بعد أن تم جمع كافة الوظائف اللازمة لملف DLL الجديد،

من كتاب تطوير نواة لينكس بواسطة الحب روبرت

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

من كتاب المؤلف

11.9.3 الربط يحتفظ خادم DHCP بجدول التعيينات بين العملاء ومعلمات التكوين الخاصة بهم. يتكون الربط من تعيين عنوان IP لكل عميل ومجموعة من التكوينات

من كتاب المؤلف

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

من كتاب المؤلف

الربط قبل أن يتمكن العميل من استدعاء إجراء بعيد، يجب أن يكون مرتبطًا بنظام بعيد يحتوي على الخادم المطلوب. وبالتالي, تنقسم المهمة الملزمة إلى قسمين:? هل تبحث عن مضيف بعيد مع الخادم المطلوب؟ العثور على

من كتاب المؤلف

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

من كتاب المؤلف

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

بدءًا من PHP 5.3.0، كانت هناك ميزة تسمى الربط الثابت المتأخر والتي يمكن استخدامها للحصول على مرجع لفئة قابلة للاستدعاء في سياق الميراث الثابت.

بتعبير أدق، يحافظ الارتباط الثابت المتأخر على اسم الفئة المحددة في "المكالمة غير المعاد توجيهها" الأخيرة. في حالة الاستدعاءات الثابتة، تكون هذه فئة محددة بشكل صريح (عادةً ما تكون على يسار عامل التشغيل :: ); في حالة المكالمات غير الثابتة، هذه هي فئة الكائن. "المكالمة المعاد توجيهها" هي مكالمة ثابتة تبدأ بـ الذات::, الأبوين::, ثابتة::أو، إذا صعدنا إلى أعلى التسلسل الهرمي للفصل، Forward_static_call(). وظيفة get_call_class()يمكن استخدامها للحصول على سلسلة باسم الفئة المطلوبة و ثابتة::يمثل نطاقها.

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

قيود الذات::

مثال رقم 1 الاستخدام الذات::

فئة أ(
صدى __فئة__ ;
}
وظيفة ثابتة عامة
امتحان()(
الذات::من();
}
}

الفئة ب تمتد أ (
وظيفة ثابتة عامة من () (
صدى __CLASS__ ؛
}
}

ب::اختبار();
?>

استخدام الربط الثابت المتأخر

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

المثال رقم 2 سهل الاستخدام ثابتة::

فئة أ(
وظيفة ثابتة عامة من () (
صدى __فئة__ ;
}
وظيفة ثابتة عامة
امتحان()(
ثابت::من(); // ينطبق الربط الثابت المتأخر هنا
}
}

الفئة ب تمتد أ (
وظيفة ثابتة عامة من () (
صدى __CLASS__ ؛
}
}

ب::اختبار();
?>

نتيجة تشغيل هذا المثال:

تعليق:

في سياق غير ثابت، ستكون الفئة التي يتم استدعاؤها هي الفئة التي ينتمي إليها مثيل الكائن. بسبب ال $هذا->سيحاول استدعاء الأساليب الخاصة من نفس النطاق باستخدام ثابتة::قد يعطي نتائج مختلفة. هناك فرق آخر هو ذلك ثابتة::يمكن أن يشير فقط إلى الحقول الثابتة للفئة.

المثال رقم 3 الاستخدام ثابتة::في سياق غير ثابت

فئة أ(
وظيفة خاصة foo() (
صدى "النجاح!\n" ;
}
اختبار الوظيفة العامة () (
$هذا -> foo();
ثابت::foo();
}
}

الفئة ب تمتد أ (
سيتم نسخ /* foo() إلى B، ومن ثم فإن نطاقه لا يزال A،
وستكون المكالمة ناجحة*/
}

تمتد الفئة C إلى A (
وظيفة خاصة foo() (
/* تم استبدال الطريقة الأصلية؛ نطاق طريقة C الجديدة */
}
}

$b = جديد B();
$ب-> اختبار();
$c = جديد C();
$c -> اختبار(); //غير صحيح
?>

نتيجة تشغيل هذا المثال:

نجاح! نجاح! نجاح! خطأ فادح: استدعاء الأسلوب الخاص C::foo() من السياق "A" في /tmp/test.php على السطر 9

تعليق:

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

المثال رقم 4: المكالمات المعاد توجيهها وغير المعاد توجيهها

الربط في C++

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

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

خذ بعين الاعتبار، على سبيل المثال، الوصف التالي للفئات والمتغيرات العامة: class Mammal

printf("غير قادر على الكلام");

صنف الكلاب : ثدييات عامة

printf("wouf wouf");

printf("wouf wouf أيضًا");

الثدييات * فيدو = كلب جديد؛

يطبع التعبير fred.speak() "غير قادر على التحدث"، لكن استدعاء fido->speak() سيطبع أيضًا "غير قادر على التحدث" لأن الطريقة المقابلة في فئة Mammal لم يتم الإعلان عنها افتراضية. التعبير fido->bark() غير مسموح به من قبل المترجم، حتى لو كان النوع الديناميكي لـ fido هو Dog. ومع ذلك، فإن النوع الثابت للمتغير هو مجرد فئة الثدييات.

إذا أضفنا كلمة افتراضية:

يتحدث الفراغ الظاهري ()

printf("غير قادر على الكلام");

ثم نحصل على النتيجة المتوقعة عند إخراج تعبير fido->speak().

أحد التغييرات الحديثة نسبيًا في لغة C++ هو إضافة تسهيلات للتعرف على الفئة الديناميكية للكائن. وهي تشكل نظام RTTI (تحديد نوع وقت التشغيل).

في نظام RTTI، يكون لكل فئة بنية مرتبطة بها من النوع typeinfo، والتي تشفر معلومات متنوعة حول الفصل. يحتوي حقل بيانات الاسم، وهو أحد حقول البيانات في هذه البنية، على اسم الفئة كسلسلة نصية. يمكن استخدام الدالة typeid لتحليل معلومات نوع البيانات. لذلك، سيقوم الأمر التالي بطباعة السلسلة "Dog" نوع البيانات الديناميكي لـ fido. في هذا المثال، من الضروري إلغاء الإشارة إلى متغير مؤشر fido بحيث تكون الوسيطة هي القيمة التي يشير إليها المؤشر، وليس المؤشر نفسه:

cout<< «fido is a» << typeid(*fido).name() << endl;

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

إذا (typeid(*fido).قبل (typeid(fred)))...

إذا (typeid(فريد).قبل (typeid(lassie)))...

قبل نظام RTTI، كانت إحدى حيل البرمجة القياسية تتمثل في ترميز الأساليب بشكل صريح في التسلسل الهرمي للفئة لتكون مثيلًا. على سبيل المثال، لاختبار قيمة متغير من النوع Animal لمعرفة ما إذا كان من النوع Cat أو النوع Dog، يمكن تحديد نظام الطرق التالي:

الظاهري int isaDog ()

الظاهري int isaCat ()

صنف الكلاب : ثدييات عامة

الظاهري int isaDog ()

صنف القط: الثدييات العامة

الظاهري int isaCat ()

يمكنك الآن استخدام الأمر fido->isaDog() لتحديد ما إذا كانت القيمة الحالية لـ fido هي قيمة من النوع Dog. إذا تم إرجاع قيمة غير الصفر، فيمكن تحويل نوع المتغير إلى نوع البيانات المطلوب.

من خلال إرجاع المؤشر بدلاً من عدد صحيح، فإننا نجمع بين اختبار الفئات الفرعية واختيار النوع. وهذا مشابه لجزء آخر من نظام RTTI يسمى Dynamic_cast، والذي سنصفه باختصار. إذا أعادت دالة في فئة Mammal مؤشرًا إلى Dog، فيجب الإعلان عن فئة Dog مسبقًا. تكون نتيجة التعيين إما مؤشرًا فارغًا أو مرجعًا صالحًا لفئة Dog. لذلك، لا يزال يتعين التحقق من النتيجة، ولكننا نلغي الحاجة إلى قالب من النوع. هذا هو مبين في المثال التالي:

كلب الطبقة؛ // الوصف الأولي

الكلب الظاهري* isaDog()

القطة الافتراضية* isaCat()

صنف الكلاب : ثدييات عامة

الكلب الظاهري* isaDog()

صنف القط: الثدييات العامة

القطة الافتراضية* isaCat()

عامل التشغيل lassie = fido->isaDog(); الآن سوف نفعل ذلك دائما. ونتيجة لذلك، يتم تعيين lassie على قيمة غير صفرية فقط إذا كان fido يحتوي على فئة Dog الديناميكية. إذا لم يكن Fido مملوكًا للكلب، فسيتم تعيين مؤشر فارغ لـ lassie.

lassie = fido->isaDog();

... // فيدو هو بالفعل من النوع Dog

... // المهمة لم تنجح

... // فيدو ليس من النوع Dog

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

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

// تحويل فقط إذا كان فيدو كلبًا

لاسي =dynamic_cast< Dog* >(فيدو)؛

// ثم تحقق مما إذا كان الإرسال ناجحًا

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

2. جزء التصميم

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

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

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

رمز البرنامج الذي يعتمد على استدعاء الأساليب الديناميكية له الخاصية تعدد الأشكال- نفس الكود يعمل بشكل مختلف اعتمادًا على نوع الكائن الذي يطلق عليه، ولكنه يفعل نفس الأشياء على مستوى التجريد المتعلق بالكود المصدر للطريقة.

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

نقل الفراغ العام إلى (int x، int y)(

علاوة على ذلك، فإن هذه الحالة لا تستفيد من تعدد الأشكال. لذلك لن نفعل ذلك.

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

لكن أساليب الإخفاء والإظهار ديناميكية، مما يعني، كما نعلم بالفعل، أن ربط اسم الطريقة وكودها القابل للتنفيذ يتم في مرحلة تنفيذ البرنامج. لذلك، فإن حقيقة تحديد هذه الأساليب في سياق فئة الشكل لا تعني أنه سيتم استدعاؤها من فئة الشكل! علاوة على ذلك، يمكنك ضمان عدم استدعاء أساليب الإخفاء والإظهار من هذه الفئة مطلقًا. دعونا نحصل على متغيرات dot1 من النوع Dot وcircle1 من النوع Circle، ويتم تعيين مراجع لكائنات من الأنواع المقابلة لها. دعونا نلقي نظرة على كيفية تصرف الاستدعاءات dot1.moveTo(x1,y1) وcircle1.moveTo(x2,y2).

عند استدعاء dot1.moveTo(x1,y1)، يتم استدعاء الأسلوب moveTo من فئة الشكل. في الواقع، لم يتم تجاوز هذه الطريقة في فئة Dot، مما يعني أنها موروثة من الشكل. في أسلوب moveTo، العبارة الأولى عبارة عن استدعاء لأسلوب الإخفاء الديناميكي. يتم أخذ تطبيق هذه الطريقة من الفئة التي يعتبر كائن dot1 الذي يستدعي هذه الطريقة مثيلًا لها. أي من فئة Dot. وبالتالي، يتم إخفاء هذه النقطة. ثم يتم تغيير إحداثيات الكائن، وبعد ذلك يتم استدعاء طريقة العرض الديناميكي. يتم أخذ تطبيق هذه الطريقة من الفئة التي يعتبر كائن dot1 الذي يستدعي هذه الطريقة مثيلًا لها. أي من فئة Dot. وهكذا، يتم عرض نقطة في الموقع الجديد.

لاستدعاء Circle1.moveTo(x2,y2) كل شيء متشابه تمامًا - يتم استدعاء الأساليب الديناميكية إخفاء وإظهار من الفئة التي يمثل كائن Circle1 مثيلًا لها، أي من فئة الدائرة. وبالتالي فهي الدائرة المخفية في المكان القديم وتظهر في المكان الجديد.

أي أنه إذا كان الجسم نقطة، فإن النقطة تتحرك. وإذا كان الجسم دائرة، فإن الدائرة تتحرك. علاوة على ذلك، إذا كتب شخص ما يومًا ما، على سبيل المثال، فئة Ellipse التي تنحدر من الدائرة، وأنشأ كائن Ellipse = new Ellipse(...)، فإن استدعاء ellipse.moveTo(...) سينقل الشكل الناقص إلى موقع جديد. وسيحدث هذا وفقًا للطريقة التي يتم بها تنفيذ أساليب الإخفاء والإظهار في فئة Ellipse. لاحظ أن الكود متعدد الأشكال لفئة الشكل الذي تم تجميعه منذ وقت طويل سيعمل. يتم ضمان تعدد الأشكال من خلال حقيقة أن الإشارات إلى هذه الأساليب لا يتم وضعها في كود أسلوب moveTo في وقت الترجمة - حيث يتم تكوينها للطرق التي تحمل هذه الأسماء من فئة كائن الاستدعاء فورًا في وقت استدعاء أسلوب moveTo.

في لغات البرمجة كائنية التوجه، يتم التمييز بين نوعين من الأساليب الديناميكية - الديناميكية الفعلية والطرق الديناميكية افتراضي. وفقًا لمبدأ التشغيل، فهي متشابهة تمامًا وتختلف فقط في ميزات التنفيذ. الاتصال بالطرق الافتراضية أسرع. يعد استدعاء الأساليب الديناميكية أبطأ، لكن جدول الأساليب الديناميكية (DMT) يستهلك ذاكرة أقل قليلاً من جدول الأساليب الافتراضية (VMT).

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

كائن الفئة الأساسية

فئة الكائن هي الفئة الأساسية لجميع فئات Java. ولذلك فإن جميع مجالاتها وأساليبها موروثة ومضمنة في جميع الفئات. تحتوي فئة الكائن على الطرق التالية:

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

مزدوج d1=1.0،d2=1.0؛

System.out.println("d1==d2 ="+(d1==d2));

System.out.println("d1.equals(d2) ="+(d1.equals(d2)));

سيعطي السطر الأول من الإخراج d1==d2 =false والسطر الثاني d1.equals(d2) =true

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

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

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

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

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

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

Object obj=new Object();

System.out.println("obj.toString() يعطي "+obj.toString());

مزدوج د = مزدوج جديد (1.0)؛

System.out.println(" d.toString() يعطي "+d.toString());

الحرف ج = "أ"؛

System.out.println("c.toString() يعطي "+c.toString());

سوف يقدم استنتاجا

يعطي obj.toString() java.lang.Object@fa9cf

d.toString() يعطي 1.0

c.toString() يعطي A

هناك أيضًا طرق إعلام (), إعلام الكل ()، والعديد من المتغيرات المثقلة للطريقة انتظر، مصممة للعمل مع المواضيع. وتناقش هذه في القسم الخاص بالتدفقات.


معلومات ذات صله.