1 / 43

תרגול מס ' 8

תרגול מס ' 8. העמסת אופרטורים בנאים הורסים והשמות המרות friend. העמסת אופרטורים. העמסת אופרטורים חדשים הגבלות דוגמה - Complex. העמסת אופרטורים. מבחינת C++ אופרטורים כמו + או * הם פונקציות רגילות ניתן להעמיס על אופרטורים בדומה לפונקציות רגילות עבור מחלקות

uri
Télécharger la présentation

תרגול מס ' 8

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. תרגול מס' 8 העמסת אופרטורים בנאים הורסים והשמות המרות friend

  2. העמסת אופרטורים העמסת אופרטורים חדשים הגבלות דוגמה - Complex מבוא לתכנות מערכות - 234122

  3. העמסת אופרטורים • מבחינת C++אופרטוריםכמו + או * הם פונקציות רגילות • ניתן להעמיס על אופרטורים בדומה לפונקציות רגילות עבור מחלקות • ניתן לקרוא לכל אופרטור מועמס בשתי דרכים • בעזרת התחביר המיוחד השמור לו, למשל עבור אופרטור+ בעזרת a+b • בעזרת תחביר של פונקציה רגילה ששמה operator<op>, למשל operator+(a,b) classComplex { doublere, im; public: Complex(double r, double i); Complexoperator+(constComplex& c) const; Complexoperator*(constComplex& c) const; }; intmain() { Complex a = Complex(1, 3.1); Complex b = Complex(1.2, 2); Complex c = b; a = b + c; // b.operator+(c) b = b + c * a; // b + (c * a) c = a * b + Complex(1, 2); return 0; } ניתן להגדיר את האופרטור כמתודהבמקרה זה הארגומנט הראשון הוא ה-this מבוא לתכנות מערכות - 234122

  4. העמסת אופרטורים • ניתן להעמיס על האופרטורים הבאים: + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- ->* , -> [] () new new[] delete delete[] • לא ניתן להעמיס על האופרטורים הבאים: :: . .* ? : sizeoftypeid • לא ניתן להגדיר אופרטורים חדשים (למשל **) • מספר הפרמטרים, קדימותםוהאסוציאטיביות שלהם נשמרים • אחד הפרמטרים לאופרטור חייב להיות מטיפוס מחלקה או enum מבוא לתכנות מערכות - 234122

  5. העמסת אופרטורים • ניתן להגדיר את האופרטורים כמתודות או כפונקציות חיצוניות • אם אופרטור המקבל פרמטרים מוגדר כמתודה הפרמטר הראשון הוא ה-thisויש להכריז על פרמטרים נוספים • אם האופרטור מוגדר כפונקציה חיצונית יש להכריז על כל הפרמטרים • לחלק מהאופרטורים קיימות גרסה אונארית וגם גרסה בינארית, למשל - או * classComplex { doublere, im; public: Complex(double r, double i); Complexoperator+(constComplex& c) const; Complexoperator-() const; // c1 = -c2; }; Complexoperator-(constComplex& c1, constComplex& c2); מבוא לתכנות מערכות - 234122

  6. העמסת אופרטורים • בגלל סיבות היסטוריות (תאימות ל-C) לכל מחלקה מוגדרים האופרטורים הבאים על ידי הקומפיילר: =(השמה), & (כתובת של) ו-, (סדרתיות) • ניתן להעמיס את האופרטורים האלה מחדש או למנוע מהמשתמש במחלקה גישה מהם על ידי הכרזתם כפרטיים. classX { private: voidoperator=(constX&); voidoperator&(); voidoperator,(constX&); //... }; • ב C++ 11 ניתן למנוע גישה גם ע"י: classX { voidoperator=(constX&) = delete; //... }; מבוא לתכנות מערכות - 234122

  7. דוגמה - מחלקת Complex classComplex { doublere, im; public: Complex(double r, double i); Complex& operator+=(constComplex& c); Complex& operator-=(constComplex& c); Complexoperator-() const; booloperator==(constComplex& c) const; }; Complexoperator+(constComplex& a, constComplex& b); Complexoperator-(constComplex& a, constComplex& b); אופרטורים הכוללים השמה כמו += מאפשרים שרשור ולכן נשמור על ההתנהגות הזו אצלנו אופרטור - אונארי יוצר מספר מרוכב חדש, לכן התוצאה מוחזרת כעותק חדש אופרטורים סימטריים נהוג להכריז מחוץ למחלקה בגלל המרות (דוגמה בהמשך התרגול) מבוא לתכנות מערכות - 234122

  8. דוגמה - מחלקת Complex Complex& Complex::operator+=(constComplex& c) { re += c.re; im += c.im; return *this; } Complex& Complex::operator-=(constComplex& c) { returnthis->operator+=(-c); // or *this += -c } Complex&Complex::operator-() const { return*(new Complex(-re, -im)); } Complexoperator+(constComplex& a, constComplex& b) { Complex c = a; return c += b; } מבוא לתכנות מערכות - 234122

  9. דוגמה - מחלקת Complex boolComplex::operator==(constComplex& c) const { return c.re == re && c.im == im; } intmain() { Complex a = Complex(1, 3.5); Complex b = Complex(1.5, 2); Complex c = a + b; // c = 2.5 + 5.5i c -= a; cout << (c == b) << endl; return 0; } מבוא לתכנות מערכות - 234122

  10. העמסת אופרטורים - הערות classArray { int* array; intsize; public://...int& operator[](intindex); };int& Array::operator[](intindex) { assert(index >= 0 && index < size); returnarray[index];} • אופרטור [] (subscripting) יכול לשמש להגדרת אוספים • חייב להיות מוגדר בתוך המחלקה • מקבל פרמטר נוסף (לא בהכרח int) • אופרטור () (function call) יכול להיות מוגדר לכל מספר של פרמטרים • חייב להיות מוגדר בתוך המחלקה • דוגמאות לשימוש ב-(): • החזרת איבר ממטריצה • החזרת תת-טווח של אוסף • עצמים המתנהגים כפונקציות (דוגמאות לכך בתרגול 12) classRandom { intseed; public:Random(int seed); intoperator()(int max);};//...Randomr(5); intrandom = r(100); מבוא לתכנות מערכות - 234122

  11. העמסת אופרטורים • לאופרטורים ++ ו--- יש שתי גרסאות: pre ו-post • כדי להגדיר++i מגדירים את האופרטור כרגיל • כדי להגדיר את i++מוספים פרמטר דמה • שימוש בהעמסת אופרטורים כדי "להמציא שפה" פוגע בקריאות הקוד • list.add(item)יותר ברור מ-list += item • שימוש באופרטורים מתאים רק עבור מקרים בהם קיימת שפה משותפת ידועה (למשל אופרטור * עבור כפל מטריצות) classX{ //... public: X& operator++(); // ++x Xoperator++(int);// x++ }; מדוע ערך ההחזרה שונה? מבוא לתכנות מערכות - 234122

  12. העמסת אופרטורים - סיכום • ניתן להתייחס לאופרטורים כפונקציות רגילות ב-C++ • ניתן להעמיס על אופרטורים קיימים כך שישמשו עבור מחלקות • את האופרטורים ניתן להגדיר כמתודות (אשר הפרמטר הראשון שלהן הוא this) וכפונקציות מחוץ למחלקות • לחלק מהאופרטורים יש הגבלות מיוחדות • מומלץ להימנע מהעמסת אופרטורים כל עוד היא לא אינטואיטיבית מבוא לתכנות מערכות - 234122

  13. בנאים, הורסים והשמות בנאים בנאי העתקה הורסים אופרטור ההשמה מבוא לתכנות מערכות - 234122

  14. בנאים והורסים - C’tors & D’tors • השימוש בפונקציות מפורשות לאתחול ושחרור עצמים (כמו ב-C) חשוף לטעויות: • המשתמש יכול לשכוח לאתחל/לשחרר את העצם • המשתמש יכול לאתחל/לשחרר את העצם פעמיים • לשם כך נוספה ב-C++ האפשרות להגדיר פונקציות ייעודיות לאתחול ושחרור עצמים - בנאים(Constructors) והורסים(Destructors) • כל עצם שנוצר ב-C++ מאותחל על ידי בנאי וכל עצם שמשוחרר מטופל על ידי הורס מבוא לתכנות מערכות - 234122

  15. בנאים - Constructors • בנאי מוגדר על ידי מתודה ששמה כשם המחלקה • לבנאי אין ערך החזרה • ניתן להגדיר מספר בנאים בהתאם לחוקי העמסת פונקציות • לבנאי שאינו מקבל פרמטרים קוראים גם default c’tor • אם לא מוגדר בנאי בצורה מפורשת למחלקה הקומפיילר ייצור בנאי חסר פרמטרים בעצמו • הבנאי הנוצר על ידי הקומפיילר קורא לבנאי חסר הפרמטרים של כל אחד מהשדות שלו • אם מוגדר בנאי כלשהו הקומפיילר לא ייצור בנאי • באתחול של מערך נקרא בנאי חסר פרמטרים לכל אחד מהאיברים • לא ניתן ליצור מערך של עצמים שאין להם בנאי מתאים classX { public: X(); X(int n);}; X global; intmain() { X x1; Xx2(5); X* ptrx1 = new X; X* ptrx2 = new X(); X* arrayx = new X[10]; arrayx[0] = X(); arrayx[1] = X(5);// ...return 0;} איזה בנאי נקרא בכל אחת מהשורות? מבוא לתכנות מערכות - 234122

  16. רשימת אתחול אתחול int n(5); int n = 5; n = 5; • חשוב להבדיל ב-C++ בין אתחול להשמה: • אתחול מתבצע על ידי בנאי • השמה מתבצעת על ידי אופרטור= • מה הבעיה בקוד הזה? • הבנאי אחראי לאתחול השדות של המחלקה, אתחול השדות מתבצע ברשימת האתחול לפני הכניסה לגוף הפונקציה על ידי קריאה לבנאים • אם עבור שדה מסוים לא מצוין כיצד יש לאתחל אותו הוא יאותחל על ידי בנאי חסר פרמטרים • אם אין בנאי מתאים לשדה תתקבל שגיאת קומפילציה אתחול השמה Complex::Complex(double r, double i) : re(r), im(i) { } Complex::Complex(double r, double i) { re = r; im = i; } re()ו-im() נקראים כאן מבוא לתכנות מערכות - 234122

  17. בנאי העתקה - Copy C’tor • לבנאי המקבל פרמטר מטיפוס העצם אותו הוא מאתחל קוראים בנאי העתקה (Copy C’tor) ויש לו מעמד מיוחד • חתימת בנאי ההעתקה היא: X::X(constX& x) • בנאי ההעתקה משמש להעברת פרמטרים והחזרת ערכים by value: • אם לא מוגדר בנאי העתקה הקומפיילר ייצור אחד בעצמו • בנאי ההעתקה שנוצר על ידי הקומפיילר מעתיק את איברי המחלקה אחד אחד בעזרת בנאי ההעתקה המתאים של כל שדה • שימו לב: בנאי העתקה נוצר על ידי הקומפיילר גם אם הוגדרו בנאים אחרים למה הפרמטר מועבר by reference? Y f(Xx, Y& y) { // X::X(const X&) // ... return y; // Y::Y(const Y&) } מבוא לתכנות מערכות - 234122

  18. בנאי העתקה - Copy C’tor • בנאי ההעתקה הנוצר אוטומטית עבור Stack ייראה כך: • מדוע הוא לא מתאים לנו? • בנאי העתקה מתאים ייראה כך: Stack::Stack(constStack& s) : data(s.data), size(s.size), nextIndex(s.nextIndex) { } Stack::Stack(constStack& s) : data(newint[s.size]), size(s.size), nextIndex(s.nextIndex) { for(int i = 0; i < nextIndex; i++) { data[i] = s.data[i]; } } מבוא לתכנות מערכות - 234122

  19. הורסים - Destructors • ההורס של מחלקה X מוגדר כמתודה ששמה ~X • הורס אינו מקבל פרמטרים ואין לו ערך חזרה • ניתן להגדיר הורס יחיד • כאשר עצם משוחרר נקרא ההורס שלו • ההורס נקרא אוטומטית - לא כותבים קריאה מפורשת להורס • אם לא מוגדר הורס הקומפיילר יגדיר אחד בעצמו • גוף הפונקציה של ההורס יהיה ריק • לאחר סיום ההורס של עצם כלשהו ייקראו ההורסים של כל השדות שלו X global; intmain() { { X x1; } X x2; X* ptrx1 = new X; X* ptrx2 = new X; delete ptrx1; X* arrayx = new X[10]; arrayx[0] = X(); delete[] arrayx; return 0; } מתי נקראים הורסים בקוד הזה? מבוא לתכנות מערכות - 234122

  20. זמני קריאה • רשימת האתחול של בנאי מבוצעת לפני הכניסה לקוד הבנאי עצמו (בתוך ה-{ }) • סדר הקריאה לאתחול השדות הוא לפי סדר הגדרתם במחלקה • לאחר ביצוע הקוד שבתוך ה-{ } בהורס ייקראו הורסים לכל שדותיו של העצם הנהרס (בסדר ההפוך מבאתחול העצם) classX { intn; public: X(int n) : n(n) { cout<< "X::X():"<< n << endl; } ~X() { cout<< "X::~X():"<< n << endl;}};classY { Xx1, x2; public: Y() : x1(1), x2(2) { cout << "Y::Y()" << endl; } ~Y() { cout << "Y::~Y()" << endl; }};intmain() { Yy; return 0;} מה יודפס? מבוא לתכנות מערכות - 234122

  21. אופרטור השמה • השמה היא הפעולה של שינוי ערכיו של משתנה קיים כך שיהיו זהים לשל משתנה אחר • ניתן להעמיס על אופרטור ההשמה שחתימתו היא: X& X::operator=(constX& x) • ניתן להעמיס על אופרטור= גם עם חתימות אחרות, אך בדרך כלל אין בכך צורך • אם לא נכתב אופרטור השמה הקומפיילר ייצור אחד בעצמו • אופרטור ההשמה שהקומפיילר יוצר קורא לאופרטורי השמה של השדות מבוא לתכנות מערכות - 234122

  22. אופרטור השמה • אופרטור השמה למחסנית Stack& Stack::operator=(constStack& s) { if (this == &s) { return *this; } delete[] data; data = newint[s.size]; size = s.size; nextIndex = s.nextIndex; for(inti=0; i< nextIndex; i++) { data[i] = s.data[i]; } return *this; } בדיקת השמה עצמית מדוע בדיקה זו הכרחית? מחיקת מידע ישן, בניגוד לבנאי העתקה צריך לנקות את המידע הקודם ביצוע הקצאות חדשות העברת המידע החזרת *this עבור שרשור מימוש זה לא מתאים עבור מקרה של חריגות (שיילמדו בהמשך), נראה מימוש טוב יותר לאופרטור= בעתיד מבוא לתכנות מערכות - 234122

  23. פונקציות הנוצרות על ידי הקומפיילר • הקומפיילר יוצר את הפונקציות הבאות בעצמו: • בנאי חסר פרמטרים: מאתחל את כל השדות של העצם בעזרת בנאי חסר פרמטרים • אם נכתב בנאי כלשהובמפורש הקומפיילר לא ייצור את פונקציה זו • ב C++ 11 ניתן לייצר בנאי זה ע"י שימוש ב =default (אחרי הגדרה של בנאי ללא ארגומנטים). • בנאי העתקה: מאתחל את כל שדות העצם בעזרת בנאי ההעתקה שלהם (והשדה המתאים בעצם אותו מעתיקים) • הקומפיילר ייצור את בנאי ההעתקה גם אם הוגדר בנאי אחר! • אופרטור השמה: קורא לאופרטור ההשמה של כל השדות של העצם • הורס: קורא להורסים של כל השדות של העצם • אין צורך לממש פונקציות שהקומפיילר יוצר בעצמו ומתאימות לנו מבוא לתכנות מערכות - 234122

  24. Big three • בין בנאי ההעתקה, ההורס ואופרטור ההשמה קיים קשר:אם צריך אחד מהם, צריך את שלושתם • איך יודעים מתי צריך לממש את הפונקציות האלו? • אם מעורבים מצביעים במחלקה בדרך כלל יש צורך בפונקציות האלו • אם יש שימוש ב-new ו-delete • אם מתבצעות הקצאות של משאבים אחרים (למשל קבצים) מבוא לתכנות מערכות - 234122

  25. בנאים, הורסים והשמות - סיכום • בנאים משמשים לאתחול עצמים • לפני הכניסה לקוד הבנאי נקראים הבנאים של כל השדות של העצם • אחרי ביצוע הקוד בהורס נקראים ההורסים של כל שדות העצם • בנאי ההעתקה משמש להעברת והחזרת ערכים by value ואתחול העתקים • אופרטור ההשמה (=) משמש להעתקת ערכים בין שני עצמים קיימים • בכתיבת אופרטור השמה חשוב לשים לב לשחרור המידע הקיים והשמות עצמיות • הקומפיילר יכתוב בעצמו בנאי חסר פרמטרים, בנאי העתקה, אופרטור השמה והורס. אין צורך לכתוב את הפונקציות האלו אם המימוש של הקומייפלר מתאים • אם צריך בנאי העתקה, אופרטור השמה או הורס לא טריוויאלים אז צריך את שלושתם מבוא לתכנות מערכות - 234122

  26. המרות אוטומטיות המרות על ידי בנאי העמסת אופרטור ההמרה בנאים מפורשים מבוא לתכנות מערכות - 234122

  27. המרות • לפעמים נרצה מספר פעולות בין טיפוסים שונים • למשל, פעולות חיבור מספר מרוכב עם ממשי Complexoperator+(constComplex&, constComplex&); Complexoperator+(constdouble&, constComplex&); Complexoperator+(constComplex&, constdouble&); • לא נרצה לממש מספר פונקציות למטרה זו • ניתן להגדיר המרות בין טיפוסים. כך למשל הקומפיילר יוכל להמיר double ל-Complex ונצטרך רק פונקציה אחת עבור החיבור Complexoperator+(constComplex&, constComplex&); • קיימות שתי דרכים להגדיר המרות ב-C++: • על ידי בנאים • על ידיד העמסת אופרטור המרה מבוא לתכנות מערכות - 234122

  28. המרה על ידי בנאי classComplex { doublere, im; public: Complex(double re) : re(re), im(0.0) {} //... }; Complexoperator+(constComplex&, constComplex&); intmain() { Complex c = 3.0; Complex c2(1.0,1.0); c = 4.0; c = c2 + 2.0; return 0; } • אם למחלקה יש בנאי המקבל פרמטר יחיד אז בנאי זה ישמש את הקומפיילר לביצוע המרות אוטומטיות • המרות המוגדרות על ידי המשתמש ישמשו את הקומפיילר לבחירה בין פונקציות מועמסות • המרות אלו מקבלות עדיפות נמוכה יותר מהמרות מובנות • בנאי עם ערכי ברירת מחדל היכול לקבל ארגומנט יחיד גם יתאים להמרות, למשל: Complex(doublere = 0.0,doubleim = 0.0) : re(re), im(im) {} מבוא לתכנות מערכות - 234122

  29. העמסת אופרטור המרה • השימוש בבנאי עבור המרה אינו מאפשר לנו להמיר עצם ממחלקה לערך בסיסי או להמיר עצם ממחלקה חדשה למחלקה ישנה • ניתן להעמיס על אופרטור המרה - שם האופרטור כשם הטיפוס אליו נרצה להמיר • את האופרטור יש להגדיר כמתודה • האופרטור מוגדר ללא ערך חזרה, טיפוס ערך החזרה נקבע לפי שם האופרטור classRational { intnum, denom; public: Rational(intnum, intdenom); // ... operator double() const; }; Rational::operator double() const { returndouble(num)/denom; } intmain() { Rational r(1,2); double d = 0.3; cout << (d+r) << endl; return 0; } מבוא לתכנות מערכות - 234122

  30. המרות אוטומטיות • מומלץ להימנע מהגדרת המרות אוטומטיות ולהעדיף הגדרת פונקציות מפורשות לביצוע המרות • המרות אוטומטיות עלולות לגרום לדו-משמעות • המרות אוטומטיות עלולות לגרום לקוד מוזר להתקמפל classRational{ //... Rational(intnum, intdenom=1); operator double() const; }; Rationaloperator+(constRational& a,constRational& b); voidf(constRational& r, int n) { r + n; // error, ambiguous} classArray { //... public: Array(int size); }; voidf(Array& array) { array = 50; // Compiles } מבוא לתכנות מערכות - 234122

  31. המרות אוטומטיות • ניתן להוסיף את המילה השמורה explicitבתחילת בנאי כדי לציין שבנאי זה לא ישמש להמרה לא מפורשת • מומלץ להכריז על בנאים המקבלים ארגומנטיחיד כ-explicit • במיוחד אם הארגומנט הוא מטיפוסבסיסי (למשל int) • הקומפיילר לא יבצע שתי המרות אוטומטיות על אותו ארגומנט, במקרים בהם זה דרוש יש להמיר בצורה מפורשת • למשל Rational לא יומר ל-double שיומר ל-Complex classArray{//...explicit Array(intsize);};voidf(Array& array) { array = 50; // error} voidf(constRational& r, constComplex& c) { Complex c3 = c + r; // error Complex c2 = c + double(r); // O.K. double -> Complex } מבוא לתכנות מערכות - 234122

  32. המרות אוטומטיות • מתודותניתן להפעיל על עצם מהטיפוס המתאים בלבד • הקומפיילר לא ימיר משתנה כדי להפעיל עליו מתודה classComplex { doublere, im; public: // ... Complex(double re); doubleabs() const; }; voidf(double& d) { d.abs(); // error Complex(d).abs(); // o.k. } מבוא לתכנות מערכות - 234122

  33. המרות אוטומטיות - סיכום • בנאים המקבלים פרמטר אחד ישמשו את הקומפיילר לביצוע המרות אוטומטיות • ניתן להעמיס על אופרטור המרה וכך להגדיר המרה מטיפוס שיצרנו לטיפוס שלא כתבנו בעצמנו • מומלץ להימנע מהגדרת המרות אוטומטיות מאחר והן יכולות לגרום לקוד פחות קריא, בעיות דו-משמעות, וקמפול מוצלח של קוד לא הגיוני • ניתן למנוע מהקומפיילר להשתמש בבנאי עבור המרות על ידי הוספת המילה explicit בהכרזה עליו • הקומפיילר לא ישתמש בהמרות כדי להפעיל מתודות מבוא לתכנות מערכות - 234122

  34. חברים - friends פונקציות חברות מחלקות חברות העמסת אופרטורי קלט ופלט מבוא לתכנות מערכות - 234122

  35. friend - פונקציות חברות classA { intn; public: A(int n) { this->n = n; } friendintgetN(constA& a); }; intgetN(constA& a) { returna.n; } intmain() { A a(5); cout << getN(a) << endl; return 0; } • לעתים נצטרך לאפשר לפונקציה חיצונית לגשת לשדות פרטיים • כדי לאפשר לפונקציה לגשת לחלקים פרטיים של מחלקה מסוימת נכריז עליה בתוך המחלקה אחרי המילה השמורה friend • הקוד שמופיע בגוף הפונקציה שהוכרזה כחברה רשאי לגשת לחלקים פרטיים של המחלקה בה הוכרז כ-friend • ניתן לממש את הפונקציה יחד עם ההכרזה או בחוץ כרגיל מבוא לתכנות מערכות - 234122

  36. friend - מחלקות חברות classA { intn; public: A(int n) { this->n = n; } friendclassB; }; classB { public: voidprintA(constA& a) { cout << a.n << endl; } }; intmain() { A a(5); Bb; b.printA(a); return 0; } • ניתן להכריז על מחלקה שלמה כ-friend • קוד אשר נכתב ב-scope של המחלקה B שהוכרזה כחברה של A רשאי לגשת לחלקים פרטיים של A • לא ניתן לבצע את ההיפך - לגשת מ-A לחלקים פרטיים של B • מומלץ להימנע משימוש ב-friend- בדרך כלל זהו תסמין של תכן רע של המערכת ועודף תלויות בין החלקים השונים בקוד • עם זאת קיימים מקרים בהם השימוש ב-friend חשוב מבוא לתכנות מערכות - 234122

  37. friends והעמסת אופרטורים voidf(constdouble& d, constComplex& c) { cout << (c == d) << (d == c) << endl; } • מה הבעיה בפונקציה f כאשראופרטור == ממומש כמתודה? • כדי לאפשר התנהגותסימטרית של האופרטורעלינו להגדיר אותוכפונקציה חיצונית • כדי לאפשר גישה לשדות עבורהפונקציה החדשה נצטרך להכריזעליה כ-friend classComplex{//...booloperator==(constComplex&) const;};boolComplex::operator==(constComplex& c) const{return c.re == re && c.im == im;} classComplex{//...friendbooloperator==(constComplex&, constComplex&);};booloperator==(constComplex& a, constComplex& b) {return a.re == b.re && a.im == b.im;} classComplex{//...booloperator==(constComplex&, constComplex&);};booloperator==(constComplex& a, constComplex& b) {return a.re == b.re && a.im == b.im;} מבוא לתכנות מערכות - 234122

  38. העמסת אופרטור הפלט • אופרטור הפלט >> מועמס כדי לאפשר הדפסה נוחה של עצמים • לא נוכל להגדיר את אופרטור >> כמתודה של מחלקה שכתבנו, מדוע? • נגדיר את אופרטור >> כפונקציה חיצונית: • במקרים רבים נצטרך גישה לשדות פרטיים - במקרים אלו נכריז על האופרטור כ-friend • את אופרטור הקלט מעמיסים בצורה דומה עבור המחלקה istream ostream& operator<<(ostream& os, constComplex& c) { constchar* sign = c.im < 0 ? "" : "+"; returnos << c.re << sign << c.im << "i"; } classComplex{//...friendostream& operator<<(ostream& os, constComplex& c);}; מבוא לתכנות מערכות - 234122

  39. מחלקת Complex המעודכנת classComplex { doublere, im; public: Complex(double re = 0.0, doubleim = 0.0); Complex& operator+=(constComplex& c); Complex& operator-=(constComplex& c); Complexoperator-() const; friendbooloperator==(constComplex& a, constComplex& b); friendostream& operator<<(ostream& os, constComplex& c); friendistream& operator>>(istream& is, Complex& c); }; Complexoperator+(constComplex& a, constComplex& b); Complexoperator-(constComplex& a, constComplex& b); מאפשר המרות ואתחול מערכים איפה בנאי ההעתקה, ההורס ואופרטור ההשמה? למה? עבור אופרטורים אלו אין צורך ב-friend מבוא לתכנות מערכות - 234122

  40. מחלקת Complex המעודכנת Complex::Complex(double re, doubleim) : re(re), im(im) {} Complex& Complex::operator+=(constComplex& c) { re += c.re; im += c.im; return *this; } Complex& Complex::operator-=(constComplex& c) { return *this += -c; } ComplexComplex::operator-() const { return Complex(-re, -im); } מבוא לתכנות מערכות - 234122

  41. מחלקת Complex המעודכנת Complexoperator+(constComplex& a, constComplex& b) { return Complex(a) += b; } Complexoperator-(constComplex& a, constComplex& b) { return Complex(a) -= b; } ostream& operator<<(ostream& os, constComplex& c) { constchar* sign = c.im < 0 ? "" : "+"; returnos << c.re << sign << c.im << "i"; } istream& operator>>(istream& is, Complex& c) { return is >> c.re >> c.im; } מחזירים את os כדי לאפשר שרשור למה אין const? מבוא לתכנות מערכות - 234122

  42. מחלקת Complex המעודכנת booloperator==(constComplex& a, constComplex& b) { return a.re == b.re && a.im == b.im; } intmain() { Complex a = 1.0; Complex b; cin >> b; cout << (-a+b) << endl; return 0; } מבוא לתכנות מערכות - 234122

  43. friend - סיכום • ניתן להכריז על פונקציה שאינה שייכת למחלקה כחברה שלה ולאפשר לה גישה לשדות פרטיים • ניתן להכריז על מחלקה כחברה של מחלקה אחרת כך שכל המתודות שלה ייחשבו כחברות • משתמשים ב-friend עבור העמסת אופרטורים שצריכים להיות מוכרזים כפונקציות חיצוניות (רק אם אכן יש צורך בכך) מבוא לתכנות מערכות - 234122

More Related