תכנות מונחה עצמים א` – תש"ע

Download Report

Transcript תכנות מונחה עצמים א` – תש"ע

‫תכנות מונחה עצמים‬
‫והנדסת תוכנה – תשע"ד‬
‫פולימורפיזם‬
‫(‪)Polymorphism‬‬
‫‪1‬‬
‫מבוא‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫פולימורפיזם (רב צורתיות) הוא מנגנון מרכזי בתכנות מונחה‬
‫עצמים‪.‬‬
‫המנגנון מאפשר לממש מבני נתונים ואלגוריתמים גנריים‪.‬‬
‫דוגמאות‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪2‬‬
‫מבנה נתונים אחד שמכיל צורות שונות (מעגל‪ ,‬מלבן‪ ,‬קטע‪ ,‬משולש‪.)...,‬‬
‫אלגוריתם להחלפת גלגל בכלי תחבורה כלשהו (משאית‪ ,‬אוטובוס‪,‬‬
‫אופנוע‪ .)...,‬קוד אחיד לכל כלי תחבורה‪ ,‬שפועל באופן שונה על כל סוג‪.‬‬
‫מימוש פונקציה מחדש‬
:‫ראינו שבירושה אפשר לממש פונקציות מחדש‬

class A {
:
void print)( { cout << “A–print”; }
};
class B : public A {
:
void print)( { cout << “B–print”; }
};
int main() {
A a;
B b;
a.print(); // A-print
b.print(); // B-print
}
3
‫המרה בירושה‬
:‫ לכן ההמרה הבאה תקינה‬,a ‫ הוא סוג של‬b ‫עצם‬

a = b; // the A part of b is assigned
a.print(); // A-print
:‫אותן המרות יתבצעו עבור מצביעים‬

A* aPtr = &a;
B* bPtr = &b;
aPtr->print(); // A-print
bPtr->print(); // B-print
aPtr = (A*)&b; // direct conversion from B* to A*
aPtr = &b // same as before
aPtr->print(); // A-print
13_5 ‫דוגמא‬

4
‫המרה בירושה (המשך)‬
‫‪‬‬
‫ההמרה ההפוכה אינה תקינה‪:‬‬
‫‪‬‬
‫דוגמא ‪13_6‬‬
‫בפרט‪ ,‬אחרי המרה מ‪ B-‬ל‪ A-‬כבר לא ניתן לגשת לחלקים של ‪:B‬‬
‫)‪b = a; // error (A is not kind of B‬‬
‫‪bPtr = &a; // the same error‬‬
‫‪‬‬
‫;‪aPtr = &b‬‬
‫‪aPtr->bFunction(); // error‬‬
‫‪‬‬
‫‪5‬‬
‫דוגמא ‪13_7‬‬
‫המרה בירושה (המשך)‬
‫;‪aPtr = &b‬‬
‫‪aPtr->print(); // A-print‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪6‬‬
‫מופעלת הפונקציה של ‪.A‬‬
‫‪ aPtr‬מצביע על עצם של ‪( B‬על החלק ה‪ A-‬של ‪ .)b‬כל המידע על‬
‫העצם ‪ b‬נמצא במקום זה בזיכרון‪.‬‬
‫מבחינה טכנית יכולנו להפעיל פונקציות של ‪ B‬באמצעות ‪.aPtr‬‬
)virtual( ‫פונקציה וירטואלית‬
‫ באמצעות‬B ‫פונקציה וירטואלית תאפשר לגשת לפונקציות של‬
:aPtr

class A {
:
virtual void print();
};
class B : public A {
:
virtual void print)(; // writing “virtual” here is optional
};
7
‫פונקציה וירטואלית (המשך)‬
‫{ )(‪int main‬‬
‫‪:‬‬
‫‪aPtr->print(); // A-print‬‬
‫‪bPtr->print(); // B-print‬‬
‫;‪aPtr = &b‬‬
‫‪aPtr->print(); // B-print‬‬
‫}‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪8‬‬
‫‪ aPtr‬מתייחס לחלק ה‪ A-‬של עצם של ‪.B‬‬
‫באמצעות המצביע ‪ aPtr‬אפשר להפעיל פונקציה של ‪.B‬‬
‫נקרא פולימורפיזם‪.‬‬
‫דוגמא ‪13_8_10‬‬
‫מנגנון הפולימורפיזם‬
?‫לשם מה נזדקק למנגנון הפולימורפיזם‬

Base
virtual void print()
Derived1
virtual void print()
Derived2
virtual void print()
Derived3
virtual void print()
Base* basePtr;
Derived1 d1; Derived2 d2; Derived3 d3; ,…
basePtr = &d1; or basePtr = &d2; or basePtr = &d1; ,…
basePtr->print(); // same code for activating different functions
9
‫דוגמאות לשימוש בפולימורפיזם‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪10‬‬
‫מבנה נתונים כללי‪ :‬מערך של צורות שונות והציור שלהן‪.‬‬
‫אלגוריתם כללי‪ :‬חישוב המקסימום הנומרי של פונקציות‪.‬‬
‫אלגוריתם כללי‪ :‬החלפת גלגל בכלי תחבורה כללי‪.‬‬
‫מבנה נתונים כללי‪ :‬רשימה משורשרת של צורות שונות‪.‬‬
‫חזרה על הנקודות המרכזיות‬
‫‪‬‬
‫הפעלת פונקציה וירטואלית בעזרת ערך‪:‬‬
‫‪‬‬
‫הפעלה פונקציה וירטואלית בעזרת מצביע‪:‬‬
‫;‪A a‬‬
‫;‪B b‬‬
‫‪a.print(); // A-print‬‬
‫;‪a = b‬‬
‫‪a.print(); // A-print‬‬
‫;‪A* aPtr‬‬
‫;‪aPtr = &a‬‬
‫‪aPtr->print(); // A-print‬‬
‫;‪aPtr = &b‬‬
‫‪aPtr->print(); // B-print‬‬
‫‪11‬‬
‫חזרה על הנקודות המרכזיות (המשך)‬
‫‪‬‬
‫הפעלה פונקציה וירטואלית בעזרת הפניה (דומה למצביע)‪:‬‬
‫;‪A& aRef1 = a‬‬
‫‪aRef1.print(); // A-print‬‬
‫;‪A& aRef2 = b‬‬
‫‪aRef2.print(); // B-print‬‬
‫‪12‬‬
‫חזרה על הנקודות המרכזיות (המשך)‬
‫‪‬‬
‫המרות לא חוקיות‪:‬‬
‫‪‬‬
‫חוסר גישה‪:‬‬
‫‪‬‬
‫אם ‪ C‬יורשת מ‪ A-‬ולא מממשת מחדש את הפונקציה )(‪print‬‬
‫(וירטואלית או לא)‪ ,‬אז הקריאה אליה מתייחסת למימוש שהוגדר ב‪-‬‬
‫‪:A‬‬
‫‪b = a; // error‬‬
‫‪bPtr = &a; // error‬‬
‫‪bRef = a; // error‬‬
‫;‪aPtr = &b‬‬
‫‪aPtr->bFunction(); // error‬‬
‫;‪aPtr = &c‬‬
‫‪aPtr->print(); // A-print‬‬
‫‪13‬‬
‫פונקציה הורסת וירטואלית‬
‫אובייקט של מחלקה יורשת עלול להשתחרר באופן חלקי בלבד‬
:‫במקרה הבא‬

class A {
public:
~A)( { cout << “A-dtor”; }
};
class B : public A {
public:
B() : _p( new int(7) ) {}
~B)( { cout << “B-dtor”; delete _p; }
private:
int* _p;
};
14
‫פונקציה הורסת וירטואלית (המשך)‬
‫{ )(‪int main‬‬
‫;)(‪A* aPtr = new B‬‬
‫;‪delete aPtr‬‬
‫}‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪15‬‬
‫מהלך התוכנית‪ :‬יודפס ‪ ,A-dtor‬וההקצאה ע"י ‪ _p‬לא תשוחרר‪.‬‬
‫פתרון‪ :‬להגדיר את הפונקציה ההורסת של ‪ A‬כפונקציה וירטואלית‪.‬‬
‫פלט תקין‪( A-dtor ,B-dtor :‬מימין לשמאל)‪.‬‬
‫פונקציה הורסת וירטואלית (המשך)‬
‫‪‬‬
‫פונקציות בונות ופונקציות השמה (‪ )set‬לא יוגדרו כוירטואליות‬
‫(עבור פונקציות בונות תתקבל שגיאת קומפילציה)‪.‬‬
‫‪‬‬
‫מומלץ להגדיר פונקציה הורסת וירטואלית במחלקת הבסיס גם‬
‫במקרה שהיא אינה עושה דבר‪ .‬הפונקציות ההורסות של המחלקות‬
‫היורשות יבצעו שחרור זיכרון מלא‪.‬‬
‫‪16‬‬
‫מחלקה אבסטרקטית‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪17‬‬
‫לעיתים נרצה להגדיר מחלקות מופשטות לחלוטין שתפקידן להוות‬
‫מכנה משותף למחלקות אחרות‪.‬‬
‫יצירת עצמים ממחלקה כזו היא חסרת משמעות‪.‬‬
‫מחלקה כזו תקרא מחלקה מורישה אבסטרקטית ( ‪abstract base‬‬
‫‪.)class‬‬
‫דוגמא‪ :‬מחלקת ‪ Shape‬מהווה מכנה משותף למרובע‪ ,‬משולש‪,‬‬
‫עיגול וכדומה‪ .‬הגדרת עצם ממחלקה זו היא חסרת משמעות‪ .‬ציור‬
‫עצם ממחלקה זו היא פעולה חסרת משמעות‪.‬‬
‫מחלקה אבסטרקטית (המשך)‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫מחלקה תוגדר להיות אבסטרקטית אם לפחות אחת הפונקציות בה‬
‫היא וירטואלית טהורה‪.‬‬
‫פונקציה וירטואלית טהורה היא פונקציה ללא מימוש במחלקה (אלא‬
‫רק במחלקות היורשות ממנה)‪.‬‬
‫הגדרת פונקציה וירטואלית טהורה‪:‬‬
‫{ ‪class Shape‬‬
‫‪:‬‬
‫;‪virtual void draw()=0‬‬
‫;}‬
‫‪‬‬
‫‪18‬‬
‫המחלקה ‪ Shape‬היא מחלקה אבסטרקטית‪.‬‬
‫מחלקה אבסטרקטית (המשך)‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪19‬‬
‫לא ניתן להגדיר עצמים של מחלקה אבסטרקטית‪ ,‬אלא רק מצביע‬
‫או הפניה לעצם ממחלקה אבסטרקטית‪.‬‬
‫כל מחלקה יורשת צריכה לספק מימוש לפונקציה הוירטואלית‬
‫טהורה‪.‬‬
‫מה ההבדל בין פונקציה וירטואלית טהורה לבין מימוש ריק?‬
‫מחלקה יורשת שאינה מממשת פונקציה וירטואלית טהורה‪ ,‬הופכת‬
‫אף היא למחלקה אבסטרקטית‪.‬‬
‫דוגמא לשימוש במחלקה אבסטרקטית ופונקציה וירטואלית טהורה‪.‬‬
‫דוגמא למחלקה יורשת שאינה מממשת פונקציה וירטואלית טהורה‪.‬‬