Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 ++C: יוצרים, הורסים ואופרטורים. 2 המחלקה Stack תזכורת class Stack { private: int* array; int size, top_index; public: Result init (int size) ; void.

Similar presentations


Presentation on theme: "1 ++C: יוצרים, הורסים ואופרטורים. 2 המחלקה Stack תזכורת class Stack { private: int* array; int size, top_index; public: Result init (int size) ; void."— Presentation transcript:

1 1 ++C: יוצרים, הורסים ואופרטורים

2 2 המחלקה Stack תזכורת class Stack { private: int* array; int size, top_index; public: Result init (int size) ; void destroy() ; Result push (int e); Result pop (); Result top(int& e); Result print() ; } ; class Stack { private: int* array; int size, top_index; public: Result init (int size) ; void destroy() ; Result push (int e); Result pop (); Result top(int& e); Result print() ; } ; init ומה יקרה אם המשתמש ישכח לקרוא ל- destroyאו ל- ???

3 3 יוצרים והורסים הבעיה - בהגדרת הטיפוס אנו משאירים פתח לטעויות של המשתמש היכול לשכוח לאתחל אובייקטים לפני השימוש בהם ( לא לקרוא ל init) דבר העלול לגרום לכך שהתוכנית תתנהג באופן בלתי צפוי, או לשכוח להרוס אובייקט וכך לגרום לדליפת זיכרון. יתר על כן, פעמים רבות נוצרים אובייקטים זמנים ( למשל, בהעברת והחזרת פרמטרים מפונקציות ) שאין למשתמש יכולות לאתחלם בצורה מפורשת. הפתרון –פונקציות אתחול והריסה שנקראות אוטומטית כאשר האובייקט נוצר וכאשר הוא אמור להיהרס. מעתה, בכל פעם שנגדיר משתנה מטיפוס Stack נדרש להעביר ליוצר את הפרמטרים הדרושים לצורך אתחול האובייקט, ובכל פעם שהאובייקט לא יהיה נגיש יותר הקומפיילר יקרא להורס אשר ישחרר את האובייקט. שמות היוצרים (constructors) כשם המחלקה. שם ההורס (destructor) כשם המחלקה שלפניה ~. class Stack { private: int* array; int size, top_index ; public: Stack (int size) ; ~Stack() ; ~Stack() ; …

4 4 Stack::Stack (int s) { array = new int[s] ; // חסרה בדיקה top_index = 0; size = s ; } Stack::~Stack() { delete[] array; } מימוש Constructors ו Destructors שימו לב C ’ tor ו D ’ tor לא מחזירים ערכים ! הערה : כמעט לכל מחלקה נדרש להגדיר יוצרים לאתחול שדותיה. הגדרת הורסים תתבצע בד ” כ עבור מחלקות המקצות זיכרון דינמי בעצמן. במקרה זה על ההורס לשחררו. אם איננו מגדירים יוצרים והורסים, מוגדרים אוטומטית יוצרים והורסים דיפולטיים.

5 5 שימוש במחסנית שיש לה יוצרים והורסים #include “Stack.h” int main() { int i; Stack s(100) ; // the c’tor is called with size 100 Stack* ps = new Stack(100); // same here if (ps == NULL) exit(1); s.push (1); s.push(213); s.pop (); s.top(i); ps->push(1); ps->push(2); ps->pop() ; delete ps ; // the d’tor is called for ps } // the d’tor is called for s

6 6 זמני קריאה של יוצרים והורסים קיימות 4 דרכים להקצאת משתנים. זמני הקריאה של היוצרים וההורסים תלויים באופן שבו הוקצה האובייקט. משתנים לוקאליים היוצר נקרא בכל פעם שהתוכנית מגיעה להכרזת המשתנה. ההורס בכל פעם שהתוכנית יוצאת מהתחום בו הוגדר המשתנה משתנים גלובאליים היוצר נקרא עם תחילת התוכנית ( לפני ה -main). ההורס עם סיום התוכנית ( לאחר סיום main). משתנים דינמיים היוצר נקרא בכל פעם שמוקצה אובייקט ע ” י new. ההורס בכל פעם שאובייקט משוחרר ע ” י delete. משתנים סטאטיים היוצר נקרא בפעם הראשונה שהתוכנית מגיע לתחום בו מוגדר המשתנה. ההורס עם סיום התוכנית.

7 7 דוגמא לזמני קריאה #include “Stack.h” Stack glbals(100); // globals c’tor is called int main() { Stack locals(50) ;// locals c’tor is called Stack* ps = new Stack(600); // ps c’tor is called Stack* ps2 = new Stack(600); // ps2 c’tor is called delete ps ;// ps destructor is called return 0;// locals destructor is called } // globals destructor is called // ps2 destructor is never called !

8 8 Advanced C ’ tors and D ’ tors class TwoStack.. { Stack s1, s2 ; public : TwoStack(int s) ;.. }; TwoStack::TwoStack (int size) : s1(100), s2(size * 5) { … } בכדי להקצות מערכים של אובייקטים ממחלקה כלשהי, לאותה מחלקה נדרש שיהיה יוצר שלא מקבל פרמטרים (או שיש לו ערכים דיפולטיים לכל הפרמטרים שלו כך שאין צורך לציין פרמטרים במפורש). ניתן להגדיר מחלקות אשר יש בהן שדות שהם עצמם עצמים של מחלקות אחרות. במקרה זה: –כאשר נוצר אובייקט של המחלקה, ראשית מאותחלים כל שדותיו. כאשר נהרס אובייקט כזה, ראשית נקרא ההורס שלו ורק אח”כ של שדותיו. –אם יוצרי השדות זקוקים לפרמטרים ניתן לבצע זאת ע”י רשימת אתחול.

9 9 רשימות אתחול הדרך המקובלת לאתחול שדות פנימיים של מחלקה. מייד אחרי הצהרת היוצר ולפני גוף היוצר (החלק שבתוך ה-{}) מופיעות נקודותיים ואז רשימה של השדות הפנימיים, כשהם מופרדים על ידי פסיקים. כל מופע של שדה ברשימת האתחול הוא למעשה קריאה לאחד מהיוצרים של השדה. לכל שדה כותבים בסוגריים את הערכים שמעבירים ליוצר שלו (ויכולים להיות תלויים בפרמטרים שהועברו ליוצר הראשי). סדר הפעלת היוצרים אינו הסדר בו הם מופיעים ברשימת האתחול אלא בו השדות מופיעים בהגדרת המחלקה. רשימת אתחול כדאית על פני "אתחול" בתוך הפונקציה כיוון שחוסכים אתחול ראשוני בזבל.

10 10 operators overloading מטרה: לאפשר עבודה טבעית ככל האפשר עם מחלקות שהגדיר המשתמש. ע”י שימוש באופרטורים, המחלקות שהמשתמש מגדיר יוכלו לעבוד עם אופרטורים כמו טיפוסים פנימיים. אם האופרטורים מממשים פעולה טבעית של המחלקה, הדבר יכול לשפר את הקריאות של הקוד. הגדרת אופרטורים מתבצעת באופן הבא: operator ( ); אופרטורים יכולים להיות methods של המחלקה או פונקציות חיצוניות. אם האופרטור מוגדר כ method אזי הארגומנט הראשון הנו תמיד האובייקט של המחלקה בו הוא מוגדר ואין צורך להעבירו.

11 11 operators overloading פונקציות עם שם מיוחד: + operator Stack& operator+=(int e) {// bad : no error checking ! array[top_index] = e; top_index++; return *this } צורת קריאה לפונקציה:  s.operator+=(5) ;  s += 5 ;

12 12 The matrix example class M { double mat[5][5]; public:... M& operator+=(const M&); friend M operator+( const M&, const M&); M operator*(const M &) ; … }; M operator+(const M&,const M&){ … int main() { M m1, m2, m3 ; …. m1+= m2 ; // calls m1.operator+=(m2); m2 = m1 + m3 ; // calls operator+(m1,m3); m3 = m2 * m2; // calls m2.operator*(m2); }

13 13 operators overloading - what can we overload? + - * / % ^ & | ! = +=-=*=/=%= ^=&=|= > >=== != =&&||++--, ->*->()[] what not ?..* :: ? : sizeof post & pre

14 14 מגבלות Operators overloading רק אופרטורים שכבר קיימים (ולא למשל אופרטור $%$). האופרטורים מקבלים את אותו מספר משתנים אותו סדר עדיפות ואותה אסוציאטיביות. הערות בנוגע להגדרת אופרטורים: ניתן להגדיר את שני סוגי הגדלה / הקטנה עצמית ע”י שמוש בפרמטר דמי. x.operator++() (pre) (++num) x.operator++(int) (post) (num++)

15 15 ערכי החזרה של אופרטורים אופרטור עבור המחלקה class יחזיר בדרך כלל איבר זמני מסוג class או רפרנס לאובייקט שעליו נקרא: class&. האופרטור יחזיר איבר זמני כאשר הפעולה יוצרת איבר חדש שאותו אנו רוצים לקבל, למשל a+b. האופרטור יחזיר רפרנס כאשר אנו רוצים לקבל את האובייקט לאחר השינוי. למשל, (a+=2)+=3 דורש שהפעולה += תחזיר רפרנס (ואז ניתן יהיה להפעיל אותה מספר פעמים ברצף על אותו אובייקט). הפעולה a++ אמורה להחזיר איבר זמני, ואילו ++a אמורה להחזיר רפרנס (מדוע?)

16 16 Overloading של אופרטורי I/O ניתן לבצע overload של אופרטורי הקלט והפלט בכדי לאפשר לבצע קלט אל ופלט מאובייקטים מטיפוסים שהוגדרו ע " י המשתמש בדומה לצורה שבה הדבר אפשרי עבור טיפוסים פנימיים. int main { int x; someclass y; otherclass z; cin >> x >> y >> z ; cout << x << y << z; }

17 17 Overloading של אופרטורי I/O class someclass { int k, j ; …. friend ostream& operator<< (ostream& os, const someclass& s1); friend istream& operator>> (istream& is, someclass& s1); }; ostream& operator<< (ostream& os,const someclass& s1) { os << “(“ << s1.k << “,” << s1.j <<“)” ; return os ; } istream& operator>> (istream& is, someclass& s1) { is >> s1.k >> s1.j ; return is ; } למה האופרטורים הוגדרו כ friend? האם זה מתחייב ? ( רמז : לא ). כיצד ניתן לשנות זאת ?

18 18 overloading של אופורטורי I/O int main() { someclass s; int i; …. cin >> i >> s ; // assume the user typed 1, 2 and 3 cout << s << i; // What does the program print? } שימו לב כי את האופרטורים האלה לא ניתן להגדיר כפונקצית member מכיוון שהארגומנט הראשון שלו צריך עצם השייך למחלקה אחרת ( ערוץ ). על הפונקציה המחזירה את האופרטור להחזיר כפלט את הערוץ כדי שיהיה אפשר להמשיך ולהזרים לו נתונים.

19 19 class String { int len;// length of string char* val;// value of string void enter(const char*); public: String();// String x; String(const char*);// String x = "abc” String(const String&);// String x = y; String& operator=(const String&);// x = y; String& operator=(const char*);// x = “abc”; ~String(); int len() const { return len; }// x.len() char operator[](int i) const ;// c = x[1] char& operator[](int i);// x[1] = ‘d’ String& operator+=(const String&);// x+=y friend bool operator==(const String&, const String&); // if (x == y) friend bool operator!=(const String&, const String&); // if (x != y) friend ostream& operator<<(ostream&, const String&);// cout << x; }; String.h בתרגול הבא

20 20 #include error(const char* st) { cerr << st << '\n'; exit (1); } void String::enter(const char* st) { if (val) delete[] val; // if string exists if (st == 0) // Null pointer val = 0 ; else if (val = new char[strlen(st)+1]) { strcpy(val, st); len = strlen(st); } else error("String: out of memory"); } String.cc (1)

21 21 String::String() { len = 0; val = 0;// Null pointer } String::String(const char* st) { len = 0; val = 0; enter(st); } String::String(const String& st) { len = 0; val = 0; enter(st.val); } String::~String() { if (val) delete[] val; } String.cc (2) למה קובעים את val ל 0 לפני הקריאה ל enter?

22 22 String& String::operator=(const char* st) { enter(st); return *this; } String& String::operator=(const String& st){ if (this!= &st) enter(st.val); return *this; } String.cc (3) בדיקת הצבה עצמית

23 23 char& String::operator[](int i) { if ((i len)) error("String: index out of range"); return val[i]; } char String::operator[](int i) const { if ((i len)) error("String: index out of range"); return val[i]; } String.cc (4)

24 24 String& String::operator+=(const String& st) { char* p; if (st.len == 0) return *this ; if ( (p = new char[len+st.len+1]) == 0) error (“String: out of memory”); strcpy(p,val); strcat(p,st.val); enter(p); delete[] p; return *this ; } String.cc (5)

25 25 bool operator==(const String& x, const String& y){ return !strcmp(x.val, y.val) ; } bool operator!=(const String& x, const String& y) { return !(x == y); } ostream& operator<<(ostream& os,const String& st) { if(st.val) os << st.val; return os; } String.cc (6)

26 26 String תוכנית הנעזרת ב int main() { String a; String b(“1234"); String c = b; // Copy constructor a = "kookoo"; // operator=(const char*) is called cout << a << "\n"; // kookoo is printed a = c; // operator=(const String&) is called cout << a << "\n";// 1234 is printed a+=b; cout << a << "\n"; // 12341234 is printed c="oooo";// If the copy constructor or assignment // were not redefined, b or a would be // changed as well }

27 27 Class String: important points שימוש בפונקציה אחת שעושה את כל העבודה האמיתית בבניה (enter) וכל השאר רק קוראות לה. דבר זה מאפשר גמישות למשתמש במחלקה תוך הימנעות מכתיבת קוד מרובה אצל המיישם. העובדה כי const הינו חלק מחתימת ה method. מדוע האופרטורים == ו =! הינם פונקציות גלובליות בעוד =+ ו [] הינם methods. אופרטור הפלט.

28 28 Class String: points to improve לא השתמשנו ברשימת אתחול ב-c’tors String::string(): val(0), len(0){} יותר מדיי פונקציות חברות, מחלקה צריכה להיות כמה שיותר סוציומטית. אפשר להוסיף פונקציות פנימיות נוספות על מנת שפונקציות ההשוואה לא יצטרכו לגשת לשדות. נחליף את: char operator[] (int i) const; const char& operator[](int i) const;ב-

29 29 מומלץ מאוד!!! http://www.yanivhamo.com/material/yanivs_string.pdf


Download ppt "1 ++C: יוצרים, הורסים ואופרטורים. 2 המחלקה Stack תזכורת class Stack { private: int* array; int size, top_index; public: Result init (int size) ; void."

Similar presentations


Ads by Google