Download presentation
Presentation is loading. Please wait.
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 Copy constructor & Assignment operator מה קורה כאשר מבצעים s1 = s2? מה קורה כאשר מבצעים Stack s1 = s2? מה ההבדל? מה הסכנה? מה קורה כאשר קוראים לפונקציה: f(Stack s) ; f(Stack& s) ; פתרון copy c’tor - לאתחול, assign. Op. - להשמה
11
11 Copy constructor & Assignment operator עבור כל מחלקה חדשה, מוגדרות באופן אוטומטי שתי פונקציות : copy constructor - זהו c’tor המשמש לאתחול של עצם אחד של המחלקה ע " י עצם אחר של אותה מחלקה. פונקציה זו גם משמשת לאתחל פרמטרים בקריאה לפונקציות, כלומר כאשר מעבירים פרמטר מסוג מחלקה מסוימת by value, ערכי השדות של הפרמטרים מאותחלים ע " י קריאה לפונקציה זאת ( כנ " ל לגבי החזרת ערכים ). ה - prototype של פונקציה זו עבור מחלקה X הוא : X(const X&) מובטח שלא נשנה את הפרמטר המועבר אי אפשר להגדיר copy constructor באמצעות copy constructor
12
12 Copy constructor & Assignment operator אופרטור ההשמה (=) - פונקציה זו משמשת להשמה של עצמים מהמחלקה זה לזה פרט לאתחול של עצם אחד של המחלקה ע " י עצם אחר של אותה מחלקה ( במקרה של אתחול ייקרא ה -copy c’tor, אפילו אם משתמשים בסימן = בביצוע האתחול ). בשני המקרים, בפונקצית ברירת המחדל ההעתקה היא איבר איבר. יש מקרים שבהם העתקה איבר איבר אינה טובה ( למשל, במקרה בו יש מצביעים כשדות במחלקה ). במקרים האלה, על המשתמש להגדיר פונקציות אלו מחדש ( דוגמא להגדרה מחדש של אופרטורים אלו נראה במחלקה Stack בהמשך )
13
13 Copy constructor & Assignment operator כלל אצבע : בכל מחלקה בה יש שדות שהם מצביעים, יש להגדיר מחדש : Copy Constructor Assignment Operator Destructor
14
14 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() ; };
15
15 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]; }
16
16 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*
17
17 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
18
18 #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)
19
19 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?
20
20 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) בדיקת הצבה עצמית
21
21 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)
22
22 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)
23
23 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)
24
24 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 }
25
25 Class String: important points שימוש בפונקציה אחת שעושה את כל העבודה האמיתית בבניה (enter) וכל השאר רק קוראות לה. דבר זה מאפשר גמישות למשתמש במחלקה תוך הימנעות מכתיבת קוד מרובה אצל המיישם. העובדה כי const הינו חלק מחתימת ה method. מדוע האופרטורים == ו =! הינם פונקציות גלובליות בעוד =+ ו [] הינם methods. אופרטור הפלט.
26
26 Class String: points to improve לא השתמשנו ברשימת אתחול ב-c’tors String::string(): val(0), len(0){} יותר מדיי פונקציות חברות, מחלקה צריכה להיות כמה שיותר סוציומטית. אפשר להוסיף פונקציות פנימיות נוספות על מנת שפונקציות ההשוואה לא יצטרכו לגשת לשדות. נחליף את: char operator[] (int i) const; const char& operator[](int i) const;ב-
27
27 מומלץ מאוד!!! http://www.yanivhamo.com/material/yanivs_string.pdf
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.