مقدمة
يبدو أن عنوان "مشكلة الحد من إمكانية الوصول إلى فئات فرعية على وظائف فئة الوالدين في Java و C ++" أكثر أكاديمية ، لكنها في الواقع مسألة يسهل تجاهلها. تسعى هذه المقالة إلى توضيح الفرق بين هذه القضية في Java و C ++.
أولاً ، سنقدم ما هو "تقليل وصول الفئات الفرعية على تغطية وظيفة فئة الوالدين". بالنسبة للميراث ، يمكن للفئات الفرعية تجاوز "الوظائف الافتراضية" للفئة الأصل - على الرغم من عدم وجود وظائف افتراضية في Java ، يمكن اعتبار جميع وظائف Java وظائف افتراضية ، لأنه يمكن تجاوز جميع وظائف Java بواسطة الفئات الفرعية. نحن هنا نقترض فقط معنى مصطلح "الوظيفة الافتراضية" ولا نحفر في تفاصيل اللغة. يسمح كل من Java و C ++ بتغيير إمكانية الوصول إلى الوظائف عند تجاوزها. ما يسمى "إمكانية الوصول" هو استخدام أحرف التحكم في الوصول مثل العام والمحمي والخاص لتعديلها للتحكم في ما إذا كان يمكن الوصول إلى الوظيفة. عادةً ما يكون ترتيب إمكانية الوصول (نظرًا لعدم وجود مفهوم للحزم في C ++ ، لا يعتبر التحكم في الوصول إلى الحزمة في الوقت الحالي ، والذي لا يؤثر على المناقشة هنا):
عام> محمي> خاص
خذ جافا كمثال:
class base {protected void sayhello () {system.out.println ("hello in base") ؛ }} class child يمتد القاعدة {public void sealhhello () {system.out.println ("Hello in Child") ؛ }} ملاحظة: وظيفة sayHello() هنا. في قاعدة الفئة الأصل ، يتم تعديل هذه الوظيفة باستخدام حرف التحكم في الوصول المحمي. والفئات الفرعية تستخدم الجمهور بدلاً من ذلك ، لن تكون هناك مشكلة. عندما تتجاوز الفئات الفرعية وظائف الفئة الأصل ، فإن توسيع نطاق الوصول لا يمثل مشكلة عادة.
يتبنى Java و C ++ استراتيجيات مختلفة عندما تقلل الفئات الفرعية من تجاوز إمكانية الوصول إلى وظائف فئة الوالدين.
أولاً ، خذ جافا كمثال وانظر إلى الكود التالي:
class base {public void sayhello () {system.out.println ("Hello in Base") ؛ }} class child يمتد القاعدة {private void sayhello () {system.out.println ("Hello in Child") ؛ }}في الكود أعلاه ، سيكون هناك خطأ في التجميع في السطر المميز 8 - لا يمكن تجميع هذا الرمز على الإطلاق! لا تسمح Java بالفئات الفرعية بتقليل إمكانية الوصول عند الكتابة فوق وظائف فئة الوالدين. أما بالنسبة للأسباب ، يمكننا استخدام مثال لتوضيحه. على سبيل المثال ، نكتب الرمز التالي خارج الفصل:
قاعدة قاعدة = قاعدة جديدة () ؛ base.sayhello () ؛ قاعدة = طفل جديد () ؛ base.sayhello () ؛
إذا كان من الممكن تجميع الكود السابق ، فهناك احتمال أنه عندما يتم الوصول إلى BASE إلى Base () ، يمكن الوصول إلى Sayhello () ، ولكن عندما لا يمكن الوصول إلى Sayhello () ، ولكن عندما يتم الوصول إلى Child () ، لا يمكن الوصول إلى Sayhello ()! من وجهة نظر جافا ، هذا تناقض ، ويجب تجنب هذه المشكلة. لذلك ، تنص جافا من منظور المترجم بأنه لا يمكننا كتابة الكود أعلاه.
بالنسبة لـ C ++ ، فإن الموقف مختلف. دعنا نلقي نظرة على مثال C ++:
قاعدة الفئة {public: Virtual void sealhello () {std :: cout << "hello in base" ؛ }} class child: public base {private: void sayhello () {std :: cout << "hello in child" ؛ }}هذا الرمز صحيح تمامًا في C ++. لاحظ أن الفئة الفرعية هنا تقلل من إمكانية الوصول عند الكتابة فوق وظائف فئة الوالدين. إذا لم تر أي مشكلة ، فيمكننا كتابة الكود التالي خارج الفصل:
طفل الطفل ؛ طفل. sayhello () ؛ // لا يمكن تجميعها لأن SEALLEHELLO () هو static_cast <base &> (child) .Sayhello () ؛ // لا يمكن تجميعها لأن sealhello () علني
فشل مكالمة السطر 2 لأنه في الطفل ، فإن sayHello() خاص ولا يمكن استدعاؤه خارجيًا. ومع ذلك ، عندما نلقي الطفل على أساس الكائن باستخدام static_cast ، تتغير الأمور - بالنسبة للقاعدة ، sayHello() عامة ، بحيث يمكن تسميتها بشكل طبيعي.
تحقيقًا لهذه الغاية ، يمكن العثور على المثال التالي في قسم الوصول إلى الوظائف الافتراضية في الفصل C ++ القياسي للوصول إلى العضو:
Class B {public: Virtual int f () ؛} ؛ class d: public b {private: int f () ؛} ؛ void f () {d d ؛ B* pb = & d ؛ d* pd = & d ؛ pb-> f () ؛ // ok: b :: f () هو عام ، d :: f () يتم استدعاء pd-> f () ؛ // خطأ: d :: f () هو خاص}في هذا الصدد ، يعطي معيار C ++ شرحًا:
يتم فحص الوصول في نقطة الاتصال باستخدام نوع التعبير المستخدم للدلالة على الكائن الذي تسمى وظيفة العضو (B* في المثال أعلاه). وصول وظيفة العضو في الفصل الذي تم تعريفه فيه (D في المثال أعلاه) بشكل عام غير معروف.
هناك نقطتان رئيسيتان لترجمة بسيطة:
ولهذا السبب ، يبدو أن المتصلين C ++ قادرون على "وظائف المكالمات" بذكاء والتي لا يمكن الوصول إليها في الأصل من خلال بعض التحولات الماهرة. مثال أكثر عملية هو: في QT ، QObject::event() عامة ، ويتم تغيير وظيفة QWidget من الفئة الفرعية event() إلى حماية. للحصول على التفاصيل ، يمكنك قراءة الكود ذي الصلة لـ QT.
باختصار ، عندما تتجاوز الفئات الفرعية وظائف الوالدين ، فإن Java يحد بشكل صارم من أن الفئات الفرعية لا يمكن أن تضيق إمكانية الوصول إلى الوظيفة ، لكن C ++ ليس لديه هذا القيد. أنا شخصياً أعتقد أنه من وجهة نظر هندسة البرمجيات ، لا شك أن لوائح Java لها أهمية أكثر هندسية ، وتكون دعوات الوظائف أكثر اتساقًا. سيؤدي معيار C ++ إلى تبسيط تطبيق التحويل البرمجي بشكل كبير ، لكنه ليس مرجعًا جيدًا للهندسة.
ملاحظة: يتطلب الإصدار الرسمي من معيار C ++ الشراء ، ولكن يمكن تنزيل المسودة مجانًا. يمكن العثور على عنوان تنزيل المسودة القياسية C ++ في الصفحة التالية: https://isocpp.org/std/the-standard
لخص
ما سبق هو المحتوى الكامل لهذه المقالة. آمل أن يكون لمحتوى هذه المقالة قيمة مرجعية معينة لدراسة أو عمل الجميع. إذا كان لديك أي أسئلة ، فيمكنك ترك رسالة للتواصل. شكرا لك على دعمك إلى wulin.com.