Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 נושאי התרגול : Copy constructor Assignment operator המרת טיפוסים אוטומטיות. תכנות גנרי - Templates.

Similar presentations


Presentation on theme: "1 נושאי התרגול : Copy constructor Assignment operator המרת טיפוסים אוטומטיות. תכנות גנרי - Templates."— Presentation transcript:

1 1 נושאי התרגול : Copy constructor Assignment operator המרת טיפוסים אוטומטיות. תכנות גנרי - Templates

2 2 Copy constructor & Assignment operator מה קורה כאשר מבצעים s1 = s2? מה קורה כאשר מבצעים Stack s1 = s2? מה ההבדל ? מה הסכנה ? מה קורה כאשר קוראים לפונקציה : f(Stack s) ; f(Stack& s) ; פתרון copy c’tor - לאתחול, assign. Op. - להשמה

3 3 Copy constructor & Assignment operator עבור כל מחלקה חדשה, מוגדרות באופן אוטומטי שתי פונקציות : copy constructor - זהו c’tor המשמש לאתחול של עצם אחד של המחלקה ע " י עצם אחר של אותה מחלקה. פונקציה זו גם משמשת לאתחל פרמטרים בקריאה לפונקציות, כלומר כאשר מעבירים פרמטר מסוג מחלקה מסוימת by value, ערכי השדות של הפרמטרים מאותחלים ע " י קריאה לפונקציה זאת ( כנ " ל לגבי החזרת ערכים ). ה - prototype של פונקציה זו עבור מחלקה X הוא : X(const X&) מובטח שלא נשנה את הפרמטר המועבר אי אפשר להגדיר copy constructor באמצעות copy constructor

4 4 Copy constructor & Assignment operator אופרטור ההשמה (=) - פונקציה זו משמשת להשמה של עצמים מהמחלקה זה לזה פרט לאתחול של עצם אחד של המחלקה ע " י עצם אחר של אותה מחלקה ( במקרה של אתחול ייקרא ה -copy c’tor, אפילו אם משתמשים בסימן = בביצוע האתחול ). בשני המקרים, בפונקצית ברירת המחדל ההעתקה היא איבר איבר. יש מקרים שבהם העתקה איבר איבר אינה טובה ( למשל, במקרה בו יש מצביעים כשדות במחלקה ). במקרים האלה, על המשתמש להגדיר פונקציות אלו מחדש ( דוגמא להגדרה מחדש של אופרטורים אלו נראה במחלקה Stack בהמשך )

5 5 Copy constructor & Assignment operator כלל אצבע : בכל מחלקה בה יש שדות שהם מצביעים, יש להגדיר מחדש : Copy Constructor Assignment Operator Destructor

6 6 The Final Stack class Stack { int* array; int top_index, size; public: Stack (int s = 100) ; Stack (const Stack&) ; Stack& operator=(const Stack&); ~Stack() ; Result push(int e); Result pop() ; Result top(int& e); void print() ; };

7 7 Copy constructor Stack::Stack(const Stack & st) { array = new int [st.size]; if (array == 0) error(“out of memory”); // assumes error exits size = st.size; top_index = st.top_index; for (int top = 0 ; top<st.top_index; top++) array[top] = st.array[top]; }

8 8 Assignment operator Stack& Stack::operator=(const Stack& st) { if (this == &st) return *this; if (st.size != size) { delete[] array; array = new int[size = st.size]; } for (int top = 0 ; top<st.top; top++) array[top] = st.array[top]; top_index = top; return *this ; } חמשת הצעדים : בדיקת הצבה עצמית שחרור משאבים הקצאת משאבים חדשים אתחול השדות החזרת this*

9 9 User Defined Type Conversions ב ++C קיימים שני סוגי המרות טיפוסים אוטומטיות : Conversion by constructor. Conversion by conversion operator.

10 10 Conversion by construction בעזרת c’tors של מחלקה T אפשר להמיר כל מחלקה אחרת או טיפוסים פנימיים של השפה כך שיהפכו לאיבר מסוג T. ההמרה מתבצעת ע ” י הגדרת Constructor המקבל ארגומנט יחיד, מהטיפוס הנ ” ל. T(class C); //converts from class C to T ההמרה תתבצע אוטומטית על ידי הקומפיילר. ההמרה מאפשרת לקרוא לפונקציות המצפות לקבל ארגומנט מטיפוס T עם ארגומנט מטיפוס C במקום. דוגמא : – במחלקה Complex : המרה מ -double ( עוד מעט ) – המרה מ -* const char למחלקה String ( תרגול קודם )

11 11 דוגמא - המחלקה Complex נניח כי רוצים להגדיר מחלקה של מספרים מרוכבים. היינו רוצים גם לאפשר את פעולות החשבון הרגילות בין מספרים מרוכבים ומספרים שלמים. דרך אחת לעשות את זה היא להגדיר את כל האפשרויות עבור כל אופרטור, בדומה לדוגמא הבאה : class complex { double re,im; public: complex (double r, double i) {re = r; im = i;} friend complex operator+(complex, complex); friend complex operator+(complex, double); friend complex operator+(double, complex); // Do the same for the rest of the operators… }; שיטה זו מייגעת ומלאה חזרות מיותרות. דרך נוספת לעשות את אותו הדבר היא להגדיר constructor ל - complex אשר מקבל פרמטר יחיד מטיפוס double. ההמרה מ double ל complex תתבצע אוטומטית באמצעות constructor זה.

12 12 דוגמא - המחלקה Complex class complex { //... complex(double r) {re = r; im = 0;} friend complex operator+(complex,complex); }; המהדר יבצע את ההמרה באופן אוטומטי. לדוגמא : main() { complex a; complex b; a=b+23; } מה שמתבצע זה : 1.23 מומר ל - complex ע " י ה - constructor. 2. מתבצע חיבור בין שני complex. 3.a מקבל את התוצאה ע " י אופרטור ההשמה המוגדר כברירת מחדל ע " י המהדר ( העתקה איבר איבר ).

13 13 Conversion by conversion operator בעזרת אופרטורים מיוחדים שמוגדרים במחלקה T אפשר להמיר את T לטיפוסים פנימיים של השפה או למחלקות אחרות שכבר הוגדרו. הדרך היחידה להגדיר המרה לטיפוסים פנימיים של השפה (למה?). המרה זו מבוצעת ע ” י הגדרת conversion operator ( מתודה של המחלקה T). operator C() ; // converts from T to C. דוגמא : class Price { int Shekel, agorot ; … public : …. operator double() { return shekel + agorot / 100.0 ;} … }; אם ניתן רצוי להיעזר ב Conversion by constructor ( של C). שימו לב שלאופרטור אין ערך החזרה אבל השתמשנו ב -return בתוכו עם ערך החזרה... מנפלאות הסינטקס של C++ ( אז מה כן מחזירים ?).

14 14 בעיות עם המרות אוטומטיות class x { //... x(int); x(char*); … }; class y{ //... y(int); …. }; class z {//... z(x); …. }; x f(x); y f(y); z g(z); void k1() { f(1);// Illegal: ambiguous f(x(1)) or f(y(1)) f(x(1)); f(y(1)); g("asdf") ; // Illegal: g(z(x("asdf"))) not tried g(z("asdf")); // O.k.: x(char*) is carried out } פרט לכך קיימות שתי בעיות עם המרות : – מתבצעת רק המרה אחת – עלולה להיווצר דו משמעות (ambiguity)

15 15 המרות ואופרטורים כזכור operators הם בעצם פונקציות. לכן גם עבורם יכולה להתבצע המרה. לדוגמה, אופרטור החיבור במחלקה Complex יכול להיקרא : c2 = c1 + 5 ; אולם אם אופרטור מוגדר כ method לא תתבצע המרה על הארגומנט הראשון שלו. בגלל בעיה זו, עדיף בד " כ להגדיר אופרטור ע " י פונק ' חיצונית. כך, הטיפול בשני האופרנדים יהיה סימטרי. class complex {... public: complex(double r) {re = r; im = 0;} operator+(const complex&); friend operator-(const complex&, const complex&); }; main() { complex c2,c1; c1=2.0+c2;// Error: 2.0 will not be converted c2=2.0-c2;// O.k.: 2.0 will be converted }

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

17 17 דוגמת המחסנית 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(); };

18 18 דוגמת המחסנית 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]; }

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

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

21 21 מחסנית גנרית 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(); };

22 22 מחסנית גנרית 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]; }

23 23 שמוש במחסנית הגנרית 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(); }

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

25 25 פונקציות גנריות 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 ?

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

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

28 28 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...


Download ppt "1 נושאי התרגול : Copy constructor Assignment operator המרת טיפוסים אוטומטיות. תכנות גנרי - Templates."

Similar presentations


Ads by Google