Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 נושאי התרגול : תכנות גנרי - Templates ירושה ופולימורפיזם.

Similar presentations


Presentation on theme: "1 נושאי התרגול : תכנות גנרי - Templates ירושה ופולימורפיזם."— Presentation transcript:

1 1 נושאי התרגול : תכנות גנרי - Templates ירושה ופולימורפיזם

2 2 תכנות גנרי - Templates משמש בעיקר כאשר בונים Containers - מחלקות אשר מכילות עצמים אחרים, אולם אין חשיבות לתכונותיו הייחודיות של אותו עצם : אותן פעולות מוגדרות על ה Container ללא קשר לעצם שבו. לדוגמא עבור מחסנית תמיד יוגדרו : –push –pop –top ללא חשיבות אם זו מחסנית של int, char או String.

3 3 דוגמת המחסנית int_stack.h: class int_stack { int top_index,size; int* array ; public: int_stack(int s) ; void pop() ; void push(int e); int top() ; int size(); }; char_stack.h: class char_stack { int top_index,size; char* array ; public: char_stack(int s) ; void pop() ; void push(char e); char top() ; int size(); }; String_stack.h: class string_stack { int top_index,size; String* array ; public: string_stack(int s) ; void pop() ; void push(String e); String top() ; int size(); };

4 4 דוגמת המחסנית int_stack.cc ( חלקי ): int_stack::int_stack (int s) { top_index = 0 ; size = s ; array = new int[s]; } int int_stack::top() { return array[top_index-1]; } string_stack.cc ( חלקי ): string_stack::string_stack (int s) { top_index = 0 ; size = s ; array = new String[s]; } String string_stack::top() { return array[top_index-1]; }

5 5 מחלקות גנריות בכדי להימנע משכפול הקוד (ליתר דיוק: כדי לבצע שכפול קוד מבוקר) נעזר ב Template. ה-Template מקבל טיפוס(ים) כפרמטר, ויכול להשתמש בפרמטר שכזה כאילו היה טיפוס רגיל. ה Template הנו בעצם תבנית ליצירת מחלקות (מעין “מקרו” מתוחכם). לאחר שהעברנו ל-Template את הטיפוס עמו הוא עובד (ע”י הוספת לשם ה-Template נקבל מחלקה רגילה לכל דבר.

6 6 מה קורה בפועל ? אנחנו כותבים מחלקה שהיא Template כמו שאנחנו כותבים מחלקה רגילה, פרט לתוספת השורה template לפני הגדרת המחלקה. כעת נוכל לכתוב את קוד המחלקה תוך שאנחנו מתייחסים ל -T כאל טיפוס כלשהו, שיועבר כפרמטר רק מאוחר יותר. בתוך התוכנית, כאשר אנחנו רוצים להשתמש ב -Template עבור ערך מסוים של T, מעבירים את הערך הזה גם כן בסוגריים משולשים ( ראו דוגמאות בהמשך ). מה שיקרה הוא שבזמן הקומפילציה יתבצע שכפול של הקוד שכתבנו עבור ה -Template ובכל מקום שבו מופיע T ייכתב הערך שהועבר ל -Template במקומו.

7 7 מחסנית גנרית stack.h: template class stack { int top_index,size; T* array ; public: stack(int s=100) ; void pop() ; void push(T e); T top() ; int size(); }; int_stack.h: class int_stack { int top_index,size; int* array ; public: int_stack(int s) ; void pop() ; void push(int e); int top() ; int size(); };

8 8 מחסנית גנרית stack.h ( חלקי ): template stack ::stack (int s) { top_index = 0 ; size = s ; array = new T[s]; } template T stack :: top() { return array[top_index-1]; } int_stack.cc ( חלקי ): int_stack::int_stack (int s) { top_index = 0 ; size = s ; array = new int[s]; } int int_stack::top() { return array[top_index-1]; }

9 9 שמוש במחסנית הגנרית int main() { stack si(100); stack sc(3); si.push(5); sc.push(“xxx”); sc.push(6); // error ! si.size(); sc.size(); } int main() { int_stack si(100); si.push(5); si.push(“xxx”); // error ! si.size(); }

10 10 Templates Templates הינם כלי מאוד שמושי לצורך הגדרת Containers. למרבה הצער המימוש של Templates ב ++C מזכיר macro חכם ולכן כל הקוד של ה Template נמצא ב header file. כפי שהוצג או כ inline function. ישנן לא רק מחלקות שהן גנריות אלא גם פונקציות גנריות.

11 11 פונקציות גנריות int major (int a, int b, int c ) { if (a == b || a == c) return a ; if (b == c) return b ; exit(1); } שימוש : int j = major (1,3,3); // ok? char c = major (‘w’,’w’,’w’);//ok? String s1 = major(s1,s2,s3); //ok ? int j = major (1,’a’,’a’); //ok ? template T major (T a,T b, T c ) { if (a == b || a == c) return a ; if (b == c) return b ; exit(1); } שימוש : int j = major (1,3,3); // ok ? char c= major (‘w’,’w’,’w’); //ok String s1 = major(s1,s2,s3); // ok? int j = major (1,’a’,’a’); //ok ?

12 12 Templates - advanced issues למרות ש ה Template לא “מתעניין” בתכונותיו המיוחדות של הטיפוס המועבר כפרמטר הוא יכול להניח הנחות לגבי קיומם של פונקציות או מתודות מסוימות בו. –אילו הנחות הניח major על T ? אילו stack ? ניתן להעביר כמה טיפוסים שונים בפרמטר: template class hashtable { bool find (const K &k, D &d) ; … };

13 13 פרמטרים ל Template ניתן להעביר ל Template פרמטר שאינו טיפוס. פרמטר יכול להיות מחרוזת, שם פונקציה או קבוע : template class Vector { T vec[Dim] ; … }; Vector v ; Vector v1; Vector v2; Vector v3 ; מה היתרון בכך שגודל ה buffer הנו חלק מהטיפוס ? ( רמז מה יקרה ב v=v3 ? )

14 14 Templates - advanced issues טיפוס שנוצר ע ” י template הינו טיפוס לכל דבר. אולם בדרך כלל כתיבת שמו המלא מסובכת לכן נעזרים בקיצור הבא : typedef stack stack_int ; אם נרצה להגדיר מחסנית של מחסניות אזי נגדיר את הטיפוס typedef stack stack_stack_int; ונעזר בו : stack_int s1(100); stack_stack_int s2(5); s2.push(s1); // in this case push should have better // accepted const T& instead of T...

15 15 הורשה ופולימורפיזם צורה ריבוע עיגול משושה

16 16 מוטיבציה : מוטיבציה : אפליקציית חלונות טיפוסית – נעזרת בפקדים (Widgets) Button Textbox Label Form

17 17 פקדים הם אובייקטים לכל דבר, ומוגדרים בספריות מיוחדות שמספקות ממשקי GUI. לפקדים שראינו יש תכונות משותפות : – קוארדינטות x,y על גבי הטופס. – אורך ורוחב. – טקסט. כמו כן אפשר לחשוב על פונקציות משותפות : – שינוי טקסט / גודל / מיקום. – הסתרה. – נעילה. – ציור.

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

19 19 אובייקט טופס (form) מכיל רשימה של כל הפקדים שנמצאים על הטופס. מה עדיף ? רשימה לכל אחד מסוגי הפקדים האפשריים, רשימה של void*, או רשימה של אובייקטים מסוג " פקד "? נניח שצריך לצייר את כל הטופס מחדש. הטופס יצטרך לקרוא לפונקצית הציור של כל אחד מהפקדים שנמצאים בו. מה עדיף ? לבצע קריאה מיוחדת לפונקצית הציור לכל סוג אפשרי של פקד, או לתת את אותה פקודת ציור לכולם, אבל ש " בדרך קסם " כל אחד מהם יפעל בצורה המתאימה לו ?

20 20 הורשה is-a הורשה הינה הדרך בה מביעים יחסי is-a בתוכנית. נעזרים בהורשה לצורך שתי מטרות שונות : code reusecode reuse - כאשר נרצה כי מספר מחלקות יהיו בעלות התנהגות זהה, כולן תירשנה ממחלקת אב משותפת אשר תממש התנהגות זו. בדרך זו נימנע משכפול של הקוד. polymorphic behaviorpolymorphic behavior - כאשר נרצה כי מספר מחלקות יחשפו ממשק זהה אך יתנהגו בצורה שונה. בשני המקרים, ההורשה תאפשר לנו להתייחס לכל אובייקט בן כאילו היה מטיפוס אובייקט האב.

21 21 מה זה is-a? נניח ש " כפתור " יורש מ " פקד ". במקרה זה אומרים ש " כפתור is-a פקד " ( באנגלית זה נשמע יותר טוב ). בעברית : " כפתור הוא סוג מיוחד של פקד ". פירוש הדבר הוא שבכל מקום שבו ניתן להשתמש בפקד, אפשר להשתמש גם בכפתור. למשל, אם יש לנו פונקציה שמקבלת פקד כפרמטר, אפשר להעביר כפתור בתור אותו פרמטר. הפונקציה תתייחס לכפתור כאל פקד ( כלומר, תתייחס לתכונות של הכפתור שמשותפות לכל הפקדים, ולא תתעניין בתכונות הייחודיות לכפתור ). כל זה נכון רק להורשה ציבורית (public) – היחידה שנלמד בקורס הזה.

22 22 הורשה לשם שימוש מחדש בקוד נרצה להוסיף למחלקה Stack הידועה את הפונקציה : (popk(int k - אשר תוציא מהמחסנית k איברים. למחלקה המשופרת נקרא MultiStack. קיימות שלוש דרכים לבצע זאת : לכתוב את MultiStack מהתחלה (cut & paste). להיעזר ב -Stack כשדה. לרשת מ -Stack.

23 23 שימוש באובייקט Stack כשדה I class MultiStack { public: MultiStack(int sz) : _s(sz) {} ~MultiStack() {} void push(int i) {_s.push(i);} void pop() { _s.pop() ; } int top() { return _s.top();} int empty() {return _s.empty();} void popk(int k) ; private: Stack _s; }; עבור כל פונקציה מקורית של Stack נדרש לכתוב פונקצית מעטפת אשר תפנה את הקריאות לאובייקט המחסנית הפנימי. לקורא הקוד לא ברור מהתוכנית כי MultiStack היא סוג מיוחד של מחסנית. כיצד נראה המימוש של popk? לא נוכל לייעל את העבודה ע ” י גישה ישרה למבני הנתונים של המחסנית ( האם friend הוא פתרון ?)

24 24 שימוש באובייקט Stack כשדה II בעיה נוספת לשיטה זו : אם יש לנו קוד כתוב אשר נעזר במחסניות ( מקבל כפרמטר מצביע או reference לאובייקט מטיפוס Stack) לא נוכל לשלוח לו MultiStack כפרמטר כי MultiStack אינו Stack. int sumAndPop(Stack & s) { int s = 0; while (! s.empty()) { s+=s.top(); s.pop() ;} return s ; }

25 25 ירושה מ Stack class MultiStack : public Stack { public: MultiStack (int sz) : Stack(sz) {} ~MultiStack() {} popk(int k) ; }; בכדי להביע כי MultiStack הינו סוג מיוחד של Stack נגדיר את MultiStack כיורש מ Stack. באופן זה כל הפונקציות של Stack מוגדרות באופן אוטומטי ( עם אותה משמעות ) עבור המחלקה MultiStack.

26 26 ירושה מ Stack ירושה כ public ( היחידה אותה נלמד ) גורמת לכך שכל משתמש של המחלקה MultiStack יוכל להיעזר במתודות של המחלקה Stack וגם במתודות של MultiStack. כל משתמש של Stack יוכל להיעזר ב - MultiStack כב -Stack רגיל.

27 27 ירושה ובונים והורסים בכל פעם שיוצרים אובייקט מהסוג היורש אנו בעצם יוצרים גם אובייקט מסוג אב. זהו המימוש ב ++C. אתחול שדות האב ( ע ” י ה -C’tor של האב, שנקרא דרך שורת האתחול של הבן ) נעשה לפני יצירת שדה כלשהו של בנו, ובפרט לפני שנקרא c’tor של הבן. הריסת האב נעשית לאחר הריסת בנו ובפרט אחרי d’tor של הבן. שדות של האב שדות של הבן אובייקט אב אובייקט בן

28 28 protected fields איך נראית הפונקציה popk? האם ניתן לגשת לשדות המימוש של המחלקה Stack ? כדי שבמחלקה היורשת ניתן יהיה לגשת לשדות של האב יש להגדיר את השדות כ protected. שדות ( ופונקציות ) private ניתנים לגישה ע ” י פונקציות של המחלקה ו friends. שדות ( ופונקציות ) protected ע ” י הקודמים + מחלקות יורשות class Stack { public : …. protected : int* array; int top_index, size ; };

29 29 גישה לשדות בהורשה ) מסוג (public Stack Private Members Protected Members Public Members MultiStack Private Members Protected Members Public Members MultiMultiStack Private Members Protected Members Public Members User

30 30 קונפליקט של שמות class A { public: … void F( ); void G(int x); }; class B: public A { public: … void F( ); void G(int x); }; void B::G(int x) { … F( ); // B גישה לפונקציה של A::G(x); // A גישה לפונקציה של מחלקת הבסיס … } כיצד ניגש ל member function של מחלקת האב כאשר יש פונקציה עם שם זהה במחלקת הבן ?

31 31 ירושה פולימורפית בדוגמאות הקודמות האב והבן חלקו את אותה התנהגות. התנהגות זו הייתה תקפה לשתי המחלקות ולכן שתי המחלקות חלקו : – את אותו ממשק : אותה חתימה וערך מוחזר של הפונקציה. – את אותו מימוש : אותו קוד פעל בשניהם. לעיתים צורת התנהגות זו לא טובה לנו ונרצה שלמרות שניתן יהיה לפנות לבן כמו שפונים לאב ( ובפרט שישתפו את הממשק ) הבן יתנהג בצורה שונה.

32 32 מחלקות מעולם החי כדוגמא למחלקות בעלות התנהגות פולימורפית. חיה משקל, גובה, ת. לידה הדפס סוגי מזון השמע קול

33 33 מחלקות בעלות התנהגות פולימורפית I class Animal { public: int getAge() ; virtual void makeSound(); private: int weight,age ; }; class Dog : public Animal{ void makeSound() ; } ; כל בעלי החיים חולקים דברים משותפים : – לכולם יש משקל, גובה, גיל – כולם אוכלים ומשמיעים קולות נרצה לתאר מחלקות מעולם החי בתוכניתנו ולהראות קשר זה יחד עם זאת לאפשר גמישות של התנהגות שונה כאשר הדבר נדרש

34 34 מחלקות בעלות התנהגות פולימורפית II ניתן במחלקה היורשת ממחלקה אחרת להגדיר מחדש פונקציות אשר הוגדרו במחלקת האב. אולם הדבר יגרום לכך שהפונקציה שתיקרא תלויה בדרך בא קראנו לה : Dog * d = new Dog(); Animal * a = d ; a->makeSound(); d->makeSound() ; אם נרצה שההתנהגות השונה תחול גם כאשר ניגשים לפונקציה דרך מצביע לאב - נעזר בפונקציות ווירטואליות.

35 35 מחלקות בעלות התנהגות פולימורפית III כאשר מגדירים פונקציה וירטואלית בעצם אומרים שהאופן המדויק בו יתנהג האובייקט תלוי בטיפוס הבן עימו אנו עובדים. למי שנעזר באובייקט האב לא משנה מה הטיפוס המדויק של הבן והוא יכול לעבוד עם כולם באופן אחיד. class Cat : public Animal{ public: void makeSound() { cout << “miao\n”; } } ; class Dog : public Animal{ public: void makeSound() { cout << “vuf vuf\n”; } } ;

36 36 מחלקות בעלות התנהגות פולימורפית IV main () { Animal* dog = new Dog(); Animal* cat = new Cat(); Animal* whoami = new Bear(); dog->makeSound(); cat->makeSound(); whoami->makeSound(); } פלט התוכנית vuf miao orrrr

37 37 שיטה חליפית להתנהגות פולימורפית ב C מקובל להיעזר בשדות type. לכל חיה היינו שומרים קוד משלה, ובכל פונקציה היינו נעזרים במשפט switch ענק שהיה אומר מה לעשות לכל טיפוס. הבעיות בגישה זו הינן שבכל פעם שנרצה להוסיף חיה למערכת נדרשת לשנות קוד בהרבה פונקציות - מאוד רגיש ובעייתי.

38 38 Abstract Classes קיימות מחלקות המייצגות רעיון מופשט ויש להן משמעות רק כבסיס למחלקות אחרות מחלקה עם מתודה Pure Virtual ( אחת או יותר ) היא מחקלה אבסטרקטית לא ניתן ליצור אובייקטים של מחלקה שכזו class Shape { public: Shape(int x, int y) : center_x(x),center_y(y){} virtual double area() const = 0; protected: int center_x, center_y ; };

39 39 class Shape { public: Shape(int x, int y) : center_x(x),center_y(y){} virtual double area() const = 0; protected: int center_x, center_y ; }; class Circle: public Shape { public: Circle(int x, int y, int r): Shape(x,y), radius(r){} double area() const { return (PI*radius*radius);} private: int radius; } ; class Rect: public Shape { public: Rect(int x, int y, int h, int w): Shape(x,y),height(h),width(w) {} double area() const {return (height*width);} private: int height, width; } ; דוגמא מסכמת : צורות


Download ppt "1 נושאי התרגול : תכנות גנרי - Templates ירושה ופולימורפיזם."

Similar presentations


Ads by Google