ݺߣ

ݺߣShare a Scribd company logo
‫בדרך לפולימורפיזם – העמסת ועקיפת פונקציות‬

 ‫בעולם התוכן של תכנות מונחה עצמים ושל כתיבת קוד בכלל קיימת שאיפה תמידית לכתוב קוד כמה שיותר‬
  ‫פולימורפי – ג' נרי כך שנוכל להשתמש ביעילות ובחסכוניות באותן שורות קוד לביצוע פעולות בעלות אופי‬
                        ‫דומה, על פני שימוש חוזר, שכפול קוד והזזה של נתונים ממקום למקום שלא לצורך.‬
     ‫פולימורפיזם, משמעותו, מצב בו קיימות מספר פונקציות שעל אף העובדה שהן חולקות את אותו השם,‬
                                                                         ‫הן מבצעות פעולות שונות.‬
      ‫ראשית, נעסוק בהבדלים בין העמסת פונקציות לעקיפת פונקציות בדרך לכתיבת קוד פולימורפי יותר.‬
              ‫הגורם העיקרי המבדיל בין "העמסת" ל"עקיפת" פונקציות הוא מיקומן בהיררכיה המחלקתית.‬
 ‫כפי שנהוג לציין בספרות, "העמסת פונקציה" נעשית בצורה אופקית, כלומר הקשר הוא בין פונקציות בעלות‬
   ‫אותו שם הנמצאות באותה דרגה של הורשה בהיררכיה המחלקתית, כאשר ההבדל היחיד בינהן הוא מספר‬
      ‫ו/או סוג הפרמטרים שהן מקבלות, בעוד "עקיפת פונקציה" נעשית בצורה אנכית, כלומר הקשר הוא בין‬
           ‫פונקציות זהות לחלוטין הנמצאות ברמות שונות בהיררכיה המחלקתית, קרי, פועלות בקשר שבין‬
                   ‫ה – ‪ base class‬ל – ‪ ,derived class‬או אם תרצו, הקשר שבין האבא והבן ( או הבת :-) ).‬

                                                                            ‫העמסת פונקציות‬
          ‫היתרון הבולט ביותר בהעמסת פונקציות הוא היכולת להשתמש "באותה פונקציה" בדרכים שונות.‬
                                                                                 ‫נראה דוגמה:‬
 ‫נניח וקיימת מחלקה בשם ‪ Robot‬ובמחלקה זו העמסנו את הפונקציה ‪ SetData‬באופן כזה שהיא יכולה לקבל‬
                                                 ‫פרמטר אחד, שני פרמטרים או לא לקבל פרמטרים בכלל:‬

‫} ;‪void SetData( ) { cout << "You have not set any data..." << endl‬‬
‫} ;‪void SetData ( int x ) { cout << "Data Recorded: " << x << endl‬‬
‫} ;‪void SetData ( int x, int y ) { cout << "Data Recorded: " << x << " " << y << endl‬‬
                                                     ‫ונניח וקיים הרובוט 2‪ ,R2D‬אז נוכל לבצע הקריאות:‬
‫;) (‪R2D2.SetData‬‬
‫;)5(‪R2D2.SetData‬‬
‫;)9,7(‪R2D2.SetData‬‬
                                                                                   ‫ולצפות לפלט הבא:‬
‫...‪You have not set any data‬‬
‫5 :‪Data Recorded‬‬
‫9 7 :‪Data Recorded‬‬
‫ברצוני להציג דוגמה עקיפה לעניין זה. סוג של אנקדוטה קטנה, שאני מרגיש שיש צורך לעמוד על קיומה.‬
      ‫נניח ו ‪ SetData‬הוא קונסטרקטור אשר ברצוננו "להעמיס" (שימו לב. אני בכוונה מציין את המרכאות על‬
‫המושג להעמיס), כך שיקבל 2 פרמטרים, פרמטר יחיד או לא יקבל פרמטרים בכלל, אך בהיעדר הזנת אחד או‬
                                     ‫יותר מהערכים, אנו נשתמש בערכי ברירת מחדל שהוגדרו מראש.‬
                                                     ‫ההצהרה על הקונסטרקטור תיעשה באופן הבא:‬
‫;) 0=‪void SetData ( int=0, int=0, int‬‬
                                                                             ‫ומימוש שלו ייראה כך:‬
‫;} ... { ) ‪void SetData ( int x, int y, int z‬‬
        ‫אם כן, לא באמת ביצענו העמסת פונקציה במקרה זה, הרי שיש לנו הצהרה אחת, ומימוש אחד עבור‬
      ‫הקונסטרקטור שבדוגמה ואמרנו קודם, שבהעמסה, אנו נממש את הפונקציה מס' פעמים עם זהות בשם‬
                                                       ‫הפונקציה ושוני במספר הפרמטרים או סוגם.‬
                                                  ‫הסיבה שבגינה ציינתי דוגמה זו, היא צד המשתמש.‬
   ‫עבור המשתמש, מדובר לכאורה בפונקציה שהועמסה, שהרי הוא יכול לקרוא לה עם שני פרמטרים, פרמטר‬
                                                          ‫בודד או ללא פרמטרים כלל. מעניין, נכון?‬
‫עקיפת פונקציות‬

‫עבור עקיפת פונקציות, נציין את הגישה הישירה לביצוע העקיפה, ובמימושים מתקדמים יותר, נעסוק בהמשך.‬
  ‫היתרון העיקרי בעקיפת פונקציות הוא היכולת "להגדיר מחדש" פונקציה שעברה בירושה מה– ‪Base Class‬‬
                ‫כך שתישאר בעלת אותו השם ואותן התכונות, אך תבצע פעולה מותאמת אישית עבור היורש.‬

                                                                      ‫נוכל לראות זאת בדוגמה הבאה:‬
                             ‫נניח וקיימת מחלקה בשם ‪ Robot‬ולה תתי מחלקות בשמות 2‪ R2D‬ו ‪.Optimus‬‬
‫כשמריצים את הפונקציה ;) (‪ WhatAreYou‬על הרובוט ב- ‪ Base Class‬הוא עונה "אני רובוט אב", ומכאן שגם‬
   ‫הבנים שלו ירשו את היכולת לענות תשובה זו, אך ברור לכל שהתשובה "אני רובוט בן" היא הנכונה עבורו.‬
   ‫כמובן שיכולנו ליצור פונקציה נוספת בשם ;) (2‪ WhatAreYou‬למשל, בה יוכל הבן לומר מי הוא באמת, אך‬
    ‫ברצוננו לשמור על המבנה הנוכחי מבלי להוסיף פונקציות מיותרות שרק יעמיסו על הקוד ועלולות לגרום‬
                                             ‫לבלבול עקב האפשרות של הבן "לשקר" ולומר שהוא אב...‬
‫על כן, עלינו לבצע "עקיפת פונקציה" על מנת להתאים אותה לצרכי הבן. אנו נבצע זאת על ידי שימוש בהגדרת‬
  ‫פונקציה הזהה לחלוטין לזו של האב (הדבר אפשרי היות והפונקציות נמצאות ברמות היררכיה שונות), ובכך‬
                                                               ‫"לדרוס" את פונקצית האב באופן הבא:‬

‫{ ‪class Robot‬‬
‫}} ;‪void WhatAreYou( ) { cout << "I'm Robot, The father of all robots" << endl‬‬
‫{ ‪class R2D2: public Robot‬‬
‫}} ;‪void WhatAreYou( ) { cout << "I'm R2D2, The child of class Robot!" << endl‬‬
‫{ ‪class Optimus: public Robot‬‬
‫}} ;‪void WhatAreYou( ) { cout << "I'm Optimus, The child of class Robot!" << endl‬‬


    ‫ובהנחה וקיימים הרובוטים ;1‪ ,Optimus robot3;, R2D2 robot2;, Robot robot‬נוכל לבצע את הקריאות‬
                                                                                           ‫הבאות:‬
‫;) (‪robot1.WhatAreYou‬‬
‫;) (‪robot2.WhatAreYou‬‬
‫;) (‪robot3.WhatAreYou‬‬
                                                                                 ‫ולצפות לפלט הבא:‬
‫!‪I'm Robot, The father of all robots‬‬
‫!‪I'm R2D2, The child of class Robot‬‬
‫!‪I'm Optimus, The child of class Robot‬‬

More Related Content

בדרך לפולימורפיזם - העמסת ועקיפת פונקציות

  • 1. ‫בדרך לפולימורפיזם – העמסת ועקיפת פונקציות‬ ‫בעולם התוכן של תכנות מונחה עצמים ושל כתיבת קוד בכלל קיימת שאיפה תמידית לכתוב קוד כמה שיותר‬ ‫פולימורפי – ג' נרי כך שנוכל להשתמש ביעילות ובחסכוניות באותן שורות קוד לביצוע פעולות בעלות אופי‬ ‫דומה, על פני שימוש חוזר, שכפול קוד והזזה של נתונים ממקום למקום שלא לצורך.‬ ‫פולימורפיזם, משמעותו, מצב בו קיימות מספר פונקציות שעל אף העובדה שהן חולקות את אותו השם,‬ ‫הן מבצעות פעולות שונות.‬ ‫ראשית, נעסוק בהבדלים בין העמסת פונקציות לעקיפת פונקציות בדרך לכתיבת קוד פולימורפי יותר.‬ ‫הגורם העיקרי המבדיל בין "העמסת" ל"עקיפת" פונקציות הוא מיקומן בהיררכיה המחלקתית.‬ ‫כפי שנהוג לציין בספרות, "העמסת פונקציה" נעשית בצורה אופקית, כלומר הקשר הוא בין פונקציות בעלות‬ ‫אותו שם הנמצאות באותה דרגה של הורשה בהיררכיה המחלקתית, כאשר ההבדל היחיד בינהן הוא מספר‬ ‫ו/או סוג הפרמטרים שהן מקבלות, בעוד "עקיפת פונקציה" נעשית בצורה אנכית, כלומר הקשר הוא בין‬ ‫פונקציות זהות לחלוטין הנמצאות ברמות שונות בהיררכיה המחלקתית, קרי, פועלות בקשר שבין‬ ‫ה – ‪ base class‬ל – ‪ ,derived class‬או אם תרצו, הקשר שבין האבא והבן ( או הבת :-) ).‬ ‫העמסת פונקציות‬ ‫היתרון הבולט ביותר בהעמסת פונקציות הוא היכולת להשתמש "באותה פונקציה" בדרכים שונות.‬ ‫נראה דוגמה:‬ ‫נניח וקיימת מחלקה בשם ‪ Robot‬ובמחלקה זו העמסנו את הפונקציה ‪ SetData‬באופן כזה שהיא יכולה לקבל‬ ‫פרמטר אחד, שני פרמטרים או לא לקבל פרמטרים בכלל:‬ ‫} ;‪void SetData( ) { cout << "You have not set any data..." << endl‬‬ ‫} ;‪void SetData ( int x ) { cout << "Data Recorded: " << x << endl‬‬ ‫} ;‪void SetData ( int x, int y ) { cout << "Data Recorded: " << x << " " << y << endl‬‬ ‫ונניח וקיים הרובוט 2‪ ,R2D‬אז נוכל לבצע הקריאות:‬ ‫;) (‪R2D2.SetData‬‬ ‫;)5(‪R2D2.SetData‬‬ ‫;)9,7(‪R2D2.SetData‬‬ ‫ולצפות לפלט הבא:‬ ‫...‪You have not set any data‬‬ ‫5 :‪Data Recorded‬‬ ‫9 7 :‪Data Recorded‬‬
  • 2. ‫ברצוני להציג דוגמה עקיפה לעניין זה. סוג של אנקדוטה קטנה, שאני מרגיש שיש צורך לעמוד על קיומה.‬ ‫נניח ו ‪ SetData‬הוא קונסטרקטור אשר ברצוננו "להעמיס" (שימו לב. אני בכוונה מציין את המרכאות על‬ ‫המושג להעמיס), כך שיקבל 2 פרמטרים, פרמטר יחיד או לא יקבל פרמטרים בכלל, אך בהיעדר הזנת אחד או‬ ‫יותר מהערכים, אנו נשתמש בערכי ברירת מחדל שהוגדרו מראש.‬ ‫ההצהרה על הקונסטרקטור תיעשה באופן הבא:‬ ‫;) 0=‪void SetData ( int=0, int=0, int‬‬ ‫ומימוש שלו ייראה כך:‬ ‫;} ... { ) ‪void SetData ( int x, int y, int z‬‬ ‫אם כן, לא באמת ביצענו העמסת פונקציה במקרה זה, הרי שיש לנו הצהרה אחת, ומימוש אחד עבור‬ ‫הקונסטרקטור שבדוגמה ואמרנו קודם, שבהעמסה, אנו נממש את הפונקציה מס' פעמים עם זהות בשם‬ ‫הפונקציה ושוני במספר הפרמטרים או סוגם.‬ ‫הסיבה שבגינה ציינתי דוגמה זו, היא צד המשתמש.‬ ‫עבור המשתמש, מדובר לכאורה בפונקציה שהועמסה, שהרי הוא יכול לקרוא לה עם שני פרמטרים, פרמטר‬ ‫בודד או ללא פרמטרים כלל. מעניין, נכון?‬
  • 3. ‫עקיפת פונקציות‬ ‫עבור עקיפת פונקציות, נציין את הגישה הישירה לביצוע העקיפה, ובמימושים מתקדמים יותר, נעסוק בהמשך.‬ ‫היתרון העיקרי בעקיפת פונקציות הוא היכולת "להגדיר מחדש" פונקציה שעברה בירושה מה– ‪Base Class‬‬ ‫כך שתישאר בעלת אותו השם ואותן התכונות, אך תבצע פעולה מותאמת אישית עבור היורש.‬ ‫נוכל לראות זאת בדוגמה הבאה:‬ ‫נניח וקיימת מחלקה בשם ‪ Robot‬ולה תתי מחלקות בשמות 2‪ R2D‬ו ‪.Optimus‬‬ ‫כשמריצים את הפונקציה ;) (‪ WhatAreYou‬על הרובוט ב- ‪ Base Class‬הוא עונה "אני רובוט אב", ומכאן שגם‬ ‫הבנים שלו ירשו את היכולת לענות תשובה זו, אך ברור לכל שהתשובה "אני רובוט בן" היא הנכונה עבורו.‬ ‫כמובן שיכולנו ליצור פונקציה נוספת בשם ;) (2‪ WhatAreYou‬למשל, בה יוכל הבן לומר מי הוא באמת, אך‬ ‫ברצוננו לשמור על המבנה הנוכחי מבלי להוסיף פונקציות מיותרות שרק יעמיסו על הקוד ועלולות לגרום‬ ‫לבלבול עקב האפשרות של הבן "לשקר" ולומר שהוא אב...‬ ‫על כן, עלינו לבצע "עקיפת פונקציה" על מנת להתאים אותה לצרכי הבן. אנו נבצע זאת על ידי שימוש בהגדרת‬ ‫פונקציה הזהה לחלוטין לזו של האב (הדבר אפשרי היות והפונקציות נמצאות ברמות היררכיה שונות), ובכך‬ ‫"לדרוס" את פונקצית האב באופן הבא:‬ ‫{ ‪class Robot‬‬ ‫}} ;‪void WhatAreYou( ) { cout << "I'm Robot, The father of all robots" << endl‬‬ ‫{ ‪class R2D2: public Robot‬‬ ‫}} ;‪void WhatAreYou( ) { cout << "I'm R2D2, The child of class Robot!" << endl‬‬ ‫{ ‪class Optimus: public Robot‬‬ ‫}} ;‪void WhatAreYou( ) { cout << "I'm Optimus, The child of class Robot!" << endl‬‬ ‫ובהנחה וקיימים הרובוטים ;1‪ ,Optimus robot3;, R2D2 robot2;, Robot robot‬נוכל לבצע את הקריאות‬ ‫הבאות:‬ ‫;) (‪robot1.WhatAreYou‬‬ ‫;) (‪robot2.WhatAreYou‬‬ ‫;) (‪robot3.WhatAreYou‬‬ ‫ולצפות לפלט הבא:‬ ‫!‪I'm Robot, The father of all robots‬‬ ‫!‪I'm R2D2, The child of class Robot‬‬ ‫!‪I'm Optimus, The child of class Robot‬‬