דילוג לתוכן
  • דף הבית
  • קטגוריות
  • פוסטים אחרונים
  • משתמשים
  • חיפוש
  • חוקי הפורום
כיווץ
תחומים

תחומים - פורום חרדי מקצועי

💡 רוצה לזכור קריאת שמע בזמן? לחץ כאן!
  1. דף הבית
  2. תכנות
  3. override תעלומה

override תעלומה

מתוזמן נעוץ נעול הועבר תכנות
8 פוסטים 2 כותבים 418 צפיות
  • מהישן לחדש
  • מהחדש לישן
  • הכי הרבה הצבעות
התחברו כדי לפרסם תגובה
נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
  • yyyY מנותק
    yyyY מנותק
    yyy
    כתב ב נערך לאחרונה על ידי
    #1

    נתונות המחלקות הבאות:

    class A
        {
            public virtual void vfoo()
            {
                Console.WriteLine("vfoo from A");
            }  
            public void foo()
            {
                Console.WriteLine("foo of A");
            }
        }
         class B : A
        {
            public override void vfoo()
            {
                Console.WriteLine("vfoo from B");
            }
            public void foo()
            {
                Console.WriteLine("foo of B");
            }
            public void baseVfoo()
            {
                base.vfoo();
            }
            public void baseFoo()
            {
                base.foo();
            }
        }
    

    להלן הקוד ב-Main ובצידו הפלט

    A a1 = new A();
                a1.vfoo(); //vfoo from A
                a1.foo();//foo from A
    B b1 = new B();                     
                b1.vfoo();//vfoo from B
                b1.foo();//vfoo from B
                b1.baseVfoo();//vfoo from A
                b1.baseFoo();//foo from A;
     A ab = new B();
                ab.foo(); //foo from A
                ab.vfoo(); //foo from B
    

    כעת לתעלומה:
    כשאני מייצר B עם זיכרון של B (שורה 4) ופונה לפונקציה מסוג virtual ב-base שלו (שורה 7), אני מקבל את הפונקציה של המחלקה המורישה, כלומר פונקציה של A.
    מסקנה: כאשר כותבים פונקציית override ביורש, היא לא דורסת את הזיכרון של המוריש.
    מאידך כאשר אני מייצר A עם זיכרון של B (שורה 9), ופונה לפונקציה הוירטואלית (שורה 11) שלהבנתי היא ב-base (כלומר בחלק הזיכרון של ה-A, כי אין בכלל גישה כעת ל-B) אני מקבל להפתעתי את הפונקציה של B.
    מסקנה: כאשר מתבצעת אפילו רק הקצאת זיכרון של B, הפונקציה הוירטואלית ב-A נדרסת.
    איך מבינים את הסתירה הזו?

    yossizY תגובה 1 תגובה אחרונה
    2
    • yossizY מנותק
      yossizY מנותק
      yossiz
      השיב לyyy ב נערך לאחרונה על ידי yossiz
      #2

      @yyy נהנתי מהשאלה, אנסה לענות כפי הנלע"ד. מקווה שאני לא טועה, וגם שאני לא מבלבל יותר מאשר מסביר.

      בכללי, אתה יותר מדי מדבר בשפה של שפות low level שלא מתאימים לשפות high level.
      בשפה כמו #C לא מדברים על הקצאות זכרון ודריסת זכרון אלא על מבנים יותר אבסטרקטיים, איך זה ממומש ברמה הנמוכה לא כל כך איכפת לנו.

      ובכן, בשפת #C צריך להיות מודע לשני מאפיינים שונים של כל משתנה. הטייפ המוצהר שלו, והטייפ האמיתי שלו (שידוע גם כן כ-runtime type).
      בד"כ כאשר קורא לפונקציה מתוך אובייקט ככה: foo.bar()‎, אז ההגדרה של bar לקוחה מתוך הטייפ המוצהר של האובייקט foo.
      אומנם, ההצהרה על פונקציה כ-virtual אומרת ל-#C: כאשר אתה מחפש את ההגדרה של פונקציה זו, תיקח אותה מהטייפ האמיתי של האובייקט ולא מהטייפ המוצהר שלו. (אם הטייפ האמיתי לא כוללת מימוש לפונקציה זו אז עולים למעלה בהיררכית הירושה עד שמגיעים למימוש).
      המילה base בכל מקרה אומרת לקחת את ההגדרה של הפונקציה מהמוריש.

      ועכשיו לשאלה שלך:

      כשאני מייצר B עם זיכרון של B

      תשתמש בניסוח המקובל. אתה בונה אובייקט מסוג B ומצביע עליו במשתנה מסוג B.

      כאשר כותבים פונקציית override ביורש, היא לא דורסת את הזיכרון של המוריש

      נכון, אין אף פעם דריסת זכרון ב-#C.

      מאידך כאשר אני מייצר A עם זיכרון של B

      כלומר אתה בונה אובייקט מסוג B ומצביע עליו עם משתנה מסוג A.

      ופונה לפונקציה הוירטואלית (שורה 11) שלהבנתי היא ב-base (כלומר בחלק הזיכרון של ה-A, כי אין בכלל גישה כעת ל-B) אני מקבל להפתעתי את הפונקציה של B

      לא נכון להגיד ש"אין בכלל גישה כעת ל-B". כמו שהסברתי #C תמיד מודעת לטייפ האמיתי של האובייקט ולטייפ המוצהר שלו. ההתנהגות בפונקציה וירטואלית הוא לקחת את המימוש מהטייפ האמיתי (שנקרא runtime type מכיון שזה דבר שנקבע בשעת ההרצה ולא בשעת כתיבה). ואם כן אין מקום להפתעות.

      אגב, הדריסה של פונקציית foo ב-B לא תקינה וגורמת לאזהרה מהמהדר. אתה אמור להצהיר על כוונתך בפירוש עם המילה new.

      📧 יוסי@מייל.קום | 🌎 בלוג | ☕ קפה

      yyyY תגובה 1 תגובה אחרונה
      5
      • yyyY מנותק
        yyyY מנותק
        yyy
        השיב לyossiz ב נערך לאחרונה על ידי
        #3

        @yossiz
        תודה על ההסבר המושקע.
        אני מבין שכאשר אתה בונה אובייקט מסוג B ומצביע עליו עם משתנה מסוג A, האובייקט ה"אמיתי" הוא B בגלל שהוא מבנה הנתונים שהוקצה (וסליחה על הגלישה שוב לכיוון אסמבלי).
        מה שלא ברור זה למה המוריש נחשב לטייפ האמיתי, יותר מהטייפ היורש?

        yossizY תגובה 1 תגובה אחרונה
        1
        • yossizY מנותק
          yossizY מנותק
          yossiz
          השיב לyyy ב נערך לאחרונה על ידי
          #4

          @yyy לא הבנתי את דבריך. איפה אתה רואה שהמוריש נחשב לטייפ האמיתי? זה לא קשור ליורש או למוריש, הטייפ האמיתי קשור לאיזה בנאי השתמשת כשבנית את האובייקט.

          📧 יוסי@מייל.קום | 🌎 בלוג | ☕ קפה

          yyyY תגובה 1 תגובה אחרונה
          1
          • yyyY מנותק
            yyyY מנותק
            yyy
            השיב לyossiz ב נערך לאחרונה על ידי
            #5

            @yossiz אמר בoverride תעלומה:

            @yyy לא הבנתי את דבריך. איפה אתה רואה שהמוריש נחשב לטייפ האמיתי? זה לא קשור ליורש או למוריש, הטייפ האמיתי קשור לאיזה בנאי השתמשת כשבנית את האובייקט.
            הבנתי.

            עכשיו איך תסביר מבחינה תיאורטית את המקרה הבא:
            יש B שיורש מ-A, ונבנה אובייקט מסוג A, המוצבע ע"י מצביע מסוג A, אלא שיש לו downCasting ל-B, באופן הבא:

            ((B)a).fooA();
            

            התוצאה תהיה Exception בזמן ריצה.
            פה חייבים לכאורה להגיע לסוגיית הקצאת הזיכרון, כלומר המערכת מנסה לגשת לזיכרון מסוג B, בשעה שההקצאה היא רק בגודל A. יש לך הסבר יותר אלגנטי?

            yossizY תגובה 1 תגובה אחרונה
            0
            • yossizY מנותק
              yossizY מנותק
              yossiz
              השיב לyyy ב נערך לאחרונה על ידי yossiz
              #6

              @yyy "הקצאת זכרון" היא מילה גסה בשפת #C או JAVA. אנחנו לא חושבים על הקצאות זכרון בשום פעם. יש אובייקטים וטייפס. ובתוך טייפס יש שני סוגים, יש את ה-runtime type (מה שקראתי למעלה ה"טייפ האמיתי" שיכול להיות דינאמי וידוע רק בשעת ריצה והוא נקבע לפי איזה בנאי השתמשת לבנות אותו) והטייפ המוצהר של המשתנה (שידועה בשעת הידור).
              הפעולה של downCasting בודקת את הטייפ האמיתי דהיינו ה-runtime type וזורקת שגיאה אם זה לא מתאים.

              (אגב, בשפת C אין בדיקות על downcasting והשגיאה נזרקת במצב הכי טוב כאשר אתה מנסה לגשת לשדה בתוך האובייקט, ובמצבים יותר גרועים באיזה מקום לא קשור בתוכנה או אף פעם, רק שתקבל ערכים לא נכונים לשדות האובייקט)

              📧 יוסי@מייל.קום | 🌎 בלוג | ☕ קפה

              yyyY תגובה 1 תגובה אחרונה
              2
              • yyyY מנותק
                yyyY מנותק
                yyy
                השיב לyossiz ב נערך לאחרונה על ידי yyy
                #7

                @yossiz אמר בoverride תעלומה:

                הפעולה של downCasting בודקת את הטייפ האמיתי דהיינו ה-runtime type וזורקת שגיאה אם זה לא מתאים.

                בודקת ביחס למה? ביחס ל-Cast, או ביחס ל-declared?

                yossizY תגובה 1 תגובה אחרונה
                0
                • yossizY מנותק
                  yossizY מנותק
                  yossiz
                  השיב לyyy ב נערך לאחרונה על ידי
                  #8

                  @yyy ביחס ל-CAST.

                  📧 יוסי@מייל.קום | 🌎 בלוג | ☕ קפה

                  תגובה 1 תגובה אחרונה
                  1
                  • dovidD dovid העביר נושא זה מ-תכנות ב-

                  בא תתחבר לדף היומי!
                  • התחברות

                  • אין לך חשבון עדיין? הרשמה

                  • התחברו או הירשמו כדי לחפש.
                  • פוסט ראשון
                    פוסט אחרון
                  0
                  • דף הבית
                  • קטגוריות
                  • פוסטים אחרונים
                  • משתמשים
                  • חיפוש
                  • חוקי הפורום