תכנות מונחה עצמים א` – תש"ע
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
לא ניתן להגדיר עצמים של מחלקה אבסטרקטית ,אלא רק מצביע
או הפניה לעצם ממחלקה אבסטרקטית.
כל מחלקה יורשת צריכה לספק מימוש לפונקציה הוירטואלית
טהורה.
מה ההבדל בין פונקציה וירטואלית טהורה לבין מימוש ריק?
מחלקה יורשת שאינה מממשת פונקציה וירטואלית טהורה ,הופכת
אף היא למחלקה אבסטרקטית.
דוגמא לשימוש במחלקה אבסטרקטית ופונקציה וירטואלית טהורה.
דוגמא למחלקה יורשת שאינה מממשת פונקציה וירטואלית טהורה.