View
225
Download
6
Embed Size (px)
Citation preview
1
:נושאי התרגול
Templatesתכנות גנרי - •
ירושה ופולימורפיזם•
2
Templatesתכנות גנרי -
- מחלקות Containersמשמש בעיקר כאשר בונים •אשר מכילות עצמים אחרים, אולם אין חשיבות
לתכונותיו הייחודיות של אותו עצם: ללא קשר Containerאותן פעולות מוגדרות על ה •
לעצם שבו. לדוגמא עבור מחסנית תמיד יוגדרו :– push– pop– top
.String או int , charללא חשיבות אם זו מחסנית של
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
דוגמת המחסנית
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
מחלקות גנריותבכדי להימנע משכפול הקוד (ליתר דיוק: כדי לבצע •
.Templateשכפול קוד מבוקר) נעזר ב מקבל טיפוס(ים) כפרמטר, ויכול Templateה-•
להשתמש בפרמטר שכזה כאילו היה טיפוס רגיל. הנו בעצם תבנית ליצירת מחלקות (מעין Templateה •
את Template“מקרו” מתוחכם). לאחר שהעברנו ל-הטיפוס עמו הוא עובד (ע”י הוספת > טיפוס < לשם
מחלקה רגילה לכל דבר. נקבלTemplateה-
6
מה קורה בפועל? כמו שאנחנו כותבים Templateאנחנו כותבים מחלקה שהיא •
>template <class Tמחלקה רגילה, פרט לתוספת השורה לפני הגדרת המחלקה.
Tכעת נוכל לכתוב את קוד המחלקה תוך שאנחנו מתייחסים ל-•כאל טיפוס כלשהו, שיועבר כפרמטר רק מאוחר יותר.
Templateבתוך התוכנית, כאשר אנחנו רוצים להשתמש ב-•, מעבירים את הערך הזה גם כן Tעבור ערך מסוים של
בסוגריים משולשים )ראו דוגמאות בהמשך(.מה שיקרה הוא שבזמן הקומפילציה יתבצע שכפול של הקוד •
ייכתב T ובכל מקום שבו מופיע Templateשכתבנו עבור ה- במקומו.Templateהערך שהועבר ל-
7
מחסנית גנריתstack.h:template <class T>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
מחסנית גנריתstack.h (חלקי):
template >class T<
stack>T<::stack (int s) {
top_index = 0 ; size = s ;
array = new T[s];
}
template >class T<
T
stack>T<:: 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
שמוש במחסנית הגנריתint main() {
stack>int< si(100);stack>char*< 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
Templates
•Templates הינם כלי מאוד שמושי לצורך .Containersהגדרת
ב +Templatesלמרבה הצער המימוש של •+C מזכיר macro חכם ולכן כל הקוד של ה
Template נמצא ב header file כפי שהוצג או ..inline functionכ
ישנן לא רק מחלקות שהן גנריות אלא גם •פונקציות גנריות.
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 >class T<
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
Templates - advanced issues
לא “מתעניין” בתכונותיו Templateלמרות ש ה •ניח יכול לההמיוחדות של הטיפוס המועבר כפרמטר הוא
הנחות לגבי קיומם של פונקציות או מתודות מסוימות בו. ? stack ? אילו T על majorאילו הנחות הניח –
ניתן להעביר כמה טיפוסים שונים בפרמטר:•template >class K, class D< class hashtable {
bool find (const K &k, D &d) ;…
};
13
Templateפרמטרים ל ל להעביר . Templateניתן פרמטר טיפוס שאינו פרמטר
, קבוע או פונקציה שם מחרוזת להיות :יכולtemplate >class T, int Dim< class Vector {
T vec[Dim] ;…
};
Vector>int, 5< v ; Vector > double, 12 < v1; Vector >Complex, 3< v2; Vector >int , 6< v3 ;
ה שגודל בכך היתרון מהטיפוס ? bufferמה חלק הנוב) יקרה מה ? (v=v3רמז
14
Templates - advanced issues הינו טיפוס לכל דבר. אולם templateטיפוס שנוצר ע”י
בדרך כלל כתיבת שמו המלא מסובכת לכן נעזרים בקיצור הבא:
typedef stack>int< stack_int ;
אם נרצה להגדיר מחסנית של מחסניות אזי נגדיר את הטיפוס
typedef stack>stack_int< 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
הורשה הורשה ופולימורפיזםופולימורפיזם
צורה
עיגולריבוע משושה
16
מוטיבציה:מוטיבציה:אפליקציית חלונות טיפוסית – נעזרת בפקדים
(Widgets)
Button
Textbox
Label
Form
17
פקדים הם אובייקטים לכל דבר, ומוגדרים •.GUIבספריות מיוחדות שמספקות ממשקי
לפקדים שראינו יש תכונות משותפות:• על גבי הטופס.x,yקוארדינטות –אורך ורוחב.–טקסט.–
כמו כן אפשר לחשוב על פונקציות משותפות:•שינוי טקסט/גודל/מיקום.–הסתרה.–נעילה.–ציור.–
18
היינו רוצים להגדיר אובייקט שייקרא "פקד" •ויכיל את כל התכונות המשותפות הללו.
לאחר מכן, כאשר מגדירים כל אחד מהפקדים •הספציפיים )כפתור, תיבת טקסט...( נוכל
להשתמש באובייקט ה"פקד" הכללי שלנו כדי לציין את התכונות המשותפות הללו.
המטרה: התוכנית שלנו תוכל לחשוב על •"כפתור" גם כעל "פקד", בלי להתעניין
בתכונות הספציפיות של "כפתור".בשביל מה זה טוב?•
19
( מכיל רשימה של כל הפקדים formאובייקט טופס )•שנמצאים על הטופס.
רשימה לכל אחד מסוגי הפקדים מה עדיף?•, או רשימה של *voidהאפשריים, רשימה של אובייקטים מסוג "פקד"?
נניח שצריך לצייר את כל הטופס מחדש. הטופס •יצטרך לקרוא לפונקצית הציור של כל אחד מהפקדים
שנמצאים בו.מה עדיף? לבצע קריאה מיוחדת לפונקצית הציור •
לכל סוג אפשרי של פקד, או לתת את אותה פקודת ציור לכולם, אבל ש"בדרך קסם" כל אחד מהם יפעל
בצורה המתאימה לו?
20
הורשה בתוכנית.is-ais-aהורשה הינה הדרך בה מביעים יחסי
:שונותנעזרים בהורשה לצורך שתי מטרות •code reusecode reuse כאשר נרצה כי מספר מחלקות יהיו -
בעלות התנהגות זהה, כולן תירשנה ממחלקת אב משותפת אשר תממש התנהגות זו. בדרך זו נימנע
משכפול של הקוד.•polymorphic behaviorpolymorphic behavior כאשר נרצה כי מספר -
מחלקות יחשפו ממשק זהה אך יתנהגו בצורה שונה. בשני המקרים, ההורשה תאפשר לנו להתייחס לכל •
אובייקט בן כאילו היה מטיפוס אובייקט האב.
21
?is-aמה זה נניח ש"כפתור" יורש מ"פקד". במקרה זה אומרים •
פקד" )באנגלית זה נשמע יותר טוב(. is-aש"כפתור בעברית: "כפתור הוא סוג מיוחד של פקד".
פירוש הדבר הוא שבכל מקום שבו ניתן להשתמש •בפקד, אפשר להשתמש גם בכפתור.
למשל, אם יש לנו פונקציה שמקבלת פקד כפרמטר, •אפשר להעביר כפתור בתור אותו פרמטר. הפונקציה תתייחס לכפתור כאל פקד )כלומר, תתייחס לתכונות
של הכפתור שמשותפות לכל הפקדים, ולא תתעניין בתכונות הייחודיות לכפתור(.
( – היחידה publicכל זה נכון רק להורשה ציבורית )•שנלמד בקורס הזה.
22
הורשה לשם שימוש מחדש בקוד
הידועה את הפונקציה:Stackנרצה להוסיף למחלקה •( popk(int k - אשר תוציא מהמחסניתk .איברים
.MultiStackלמחלקה המשופרת נקרא
קיימות שלוש דרכים לבצע זאת:(.cut & paste מהתחלה )MultiStack לכתוב את • כשדה.Stack להיעזר ב-•.Stack לרשת מ-•
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
כשדה Stackשימוש באובייקט II
בעיה נוספת לשיטה זו: אם יש לנו קוד כתוב אשר נעזר במחסניות )מקבל כפרמטר מצביע או
reference לאובייקט מטיפוס Stack לא נוכל לשלוח ) .Stackאינו MultiStack כפרמטר כי MultiStackלו
int sumAndPop(Stack & s){
int s = 0 ;
while (! s.empty()) { s+=s.top(); s.pop() ;}
return s;
}
25
Stackירושה מ
class MultiStack :
public Stackpublic Stack{ {
public:
MultiStack (int sz) :
Stack(sz)Stack(sz)}{ }{
~ MultiStack }{ )(
popk(int k);
;}
בכדי להביע כי •MultiStack הינו סוג נגדיר Stackמיוחד של
כיורש מ MultiStackאת Stack.
באופן זה כל הפונקציות • מוגדרות Stackשל
באופן אוטומטי )עם אותה משמעות( עבור
. MultiStackהמחלקה
26
Stackירושה מ
)היחידה אותה נלמד( גורמת publicירושה כ • MultiStackלכך שכל משתמש של המחלקה
Stackיוכל להיעזר במתודות של המחלקה .MultiStack במתודות של וגם
יוכל להיעזר ב- Stackכל משתמש של •MultiStack-כב Stack.רגיל
27
ירושה ובונים והורסיםבכל פעם שיוצרים אובייקט מהסוג היורש אנו בעצם •
.Cיוצרים גם אובייקט מסוג אב. זהו המימוש ב ++
של האב, שנקרא C’torאתחול שדות האב )ע”י ה-•דרך שורת האתחול של הבן( נעשה לפני יצירת שדה
של הבן.c’torכלשהו של בנו, ובפרט לפני שנקרא הריסת האב נעשית לאחר הריסת בנו ובפרט אחרי •
d’tor.של הבן
שדות של
האב
שדות הבן של
אובייקט אב
בן אובייקט
28
protected fields
איך נראית הפונקציה •popk האם ניתן ?
לגשת לשדות המימוש ?Stackשל המחלקה
כדי שבמחלקה •היורשת ניתן יהיה
לגשת לשדות של האב יש להגדיר את השדות
.protectedכ
privateשדות )ופונקציות( •ניתנים לגישה ע”י פונקציות של
.friendsהמחלקה ו protected( ופונקציות)שדות •
ע”י הקודמים + מחלקות יורשות
class Stack {
public.… :
protected:
int* array ;
int top_index, size;
;}
29
גישה לשדות בהורשה גישה לשדות בהורשה publicpublic ) )מסוגמסוג((
StackStack Private Members Protected Members Public Members
MultiStackMultiStack Private MembersProtected Members Public Members
MultiMultiStackMultiMultiStack Private MembersProtected Members Public Members
User
31
ירושה פולימורפיתבדוגמאות הקודמות האב והבן חלקו את אותה •
התנהגות. התנהגות זו הייתה תקפה לשתי המחלקות ולכן שתי המחלקות חלקו:
את אותו ממשק: אותה חתימה וערך מוחזר של הפונקציה.–את אותו מימוש: אותו קוד פעל בשניהם.–
לעיתים צורת התנהגות זו לא טובה לנו ונרצה •שלמרות שניתן יהיה לפנות לבן כמו שפונים לאב )ובפרט שישתפו את הממשק( הבן יתנהג בצורה
שונה.
32
מחלקות מעולם החי כדוגמא למחלקות בעלות התנהגות
פולימורפית. חיה
. לידה , , ת גובה משקלמזון סוגי הדפס
קול השמע
33
מחלקות בעלות התנהגות Iפולימורפית
class Animal{
public:
int getAge; )(
virtual void makeSound;)(
private:
int weight,age;
;}
class Dog : public Animal{
void makeSound; )(
; }
כל בעלי החיים חולקים •דברים משותפים:
לכולם יש משקל, גובה, –גיל
כולם אוכלים ומשמיעים –קולות
נרצה לתאר מחלקות •מעולם החי בתוכניתנו
ולהראות קשר זהיחד עם זאת לאפשר •
גמישות של התנהגות שונה כאשר הדבר נדרש
34
מחלקות בעלות התנהגות IIפולימורפית
ניתן במחלקה היורשת ממחלקה אחרת •להגדיר מחדש פונקציות אשר הוגדרו
במחלקת האב. אולם הדבר יגרום לכך שהפונקציה שתיקרא •
תלויה בדרך בא קראנו לה:Dog * d = new Dog;)(Animal * a = d ; a-<makeSound(); d-<makeSound; )(
אם נרצה שההתנהגות השונה תחול גם •כאשר ניגשים לפונקציה דרך מצביע לאב -
נעזר בפונקציות ווירטואליות.
35
מחלקות בעלות התנהגות IIIפולימורפית
כאשר מגדירים פונקציה וירטואלית בעצם אומרים •שהאופן המדויק בו יתנהג האובייקט תלוי בטיפוס הבן
עימו אנו עובדים. למי שנעזר באובייקט האב לא משנה מה הטיפוס המדויק של הבן והוא יכול לעבוד עם כולם
באופן אחיד.
class Cat : public Animal{
public:
void makeSound{ )(
cout >> “miao\n;”
}
; }
class Dog : public Animal{
public:
void makeSound{ )(
cout >> “vuf vuf\n;”
}
; }
36
מחלקות בעלות התנהגות פולימורפית IV
main{ )(
Animal* dog = new Dog;)(
Animal* cat = new Cat;)(
Animal* whoami = new Bear;)(
dog-<makeSound;)(
cat-<makeSound;)(
whoami-<makeSound;)(
}
פלט התוכנית
vuf vuf
miao
orrrr
37
שיטה חליפית להתנהגות פולימורפית
. לכל חיה type מקובל להיעזר בשדות Cב •היינו שומרים קוד משלה, ובכל פונקציה
ענק שהיה switchהיינו נעזרים במשפט אומר מה לעשות לכל טיפוס.
הבעיות בגישה זו הינן שבכל פעם שנרצה •להוסיף חיה למערכת נדרשת לשנות קוד
בהרבה פונקציות - מאוד רגיש ובעייתי.
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
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;
; }
דוגמא מסכמת: צורותדוגמא מסכמת: צורות