450 likes | 779 Views
תרגול מס ' 8. העמסת אופרטורים בנאים הורסים והשמות המרות friend. העמסת אופרטורים. העמסת אופרטורים חדשים הגבלות דוגמה - Complex. העמסת אופרטורים. מבחינת C++ אופרטורים כמו + או * הם פונקציות רגילות ניתן להעמיס על אופרטורים בדומה לפונקציות רגילות עבור מחלקות
E N D
תרגול מס' 8 העמסת אופרטורים בנאים הורסים והשמות המרות friend
העמסת אופרטורים העמסת אופרטורים חדשים הגבלות דוגמה - Complex מבוא לתכנות מערכות - 234122
העמסת אופרטורים • מבחינת 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
העמסת אופרטורים • ניתן להעמיס על האופרטורים הבאים: + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- ->* , -> [] () new new[] delete delete[] • לא ניתן להעמיס על האופרטורים הבאים: :: . .* ? : sizeoftypeid • לא ניתן להגדיר אופרטורים חדשים (למשל **) • מספר הפרמטרים, קדימותםוהאסוציאטיביות שלהם נשמרים • אחד הפרמטרים לאופרטור חייב להיות מטיפוס מחלקה או enum מבוא לתכנות מערכות - 234122
העמסת אופרטורים • ניתן להגדיר את האופרטורים כמתודות או כפונקציות חיצוניות • אם אופרטור המקבל פרמטרים מוגדר כמתודה הפרמטר הראשון הוא ה-thisויש להכריז על פרמטרים נוספים • אם האופרטור מוגדר כפונקציה חיצונית יש להכריז על כל הפרמטרים • לחלק מהאופרטורים קיימות גרסה אונארית וגם גרסה בינארית, למשל - או * classComplex { doublere, im; public: Complex(double r, double i); Complexoperator+(constComplex& c) const; Complexoperator-() const; // c1 = -c2; }; Complexoperator-(constComplex& c1, constComplex& c2); מבוא לתכנות מערכות - 234122
העמסת אופרטורים • בגלל סיבות היסטוריות (תאימות ל-C) לכל מחלקה מוגדרים האופרטורים הבאים על ידי הקומפיילר: =(השמה), & (כתובת של) ו-, (סדרתיות) • ניתן להעמיס את האופרטורים האלה מחדש או למנוע מהמשתמש במחלקה גישה מהם על ידי הכרזתם כפרטיים. classX { private: voidoperator=(constX&); voidoperator&(); voidoperator,(constX&); //... }; • ב C++ 11 ניתן למנוע גישה גם ע"י: classX { voidoperator=(constX&) = delete; //... }; מבוא לתכנות מערכות - 234122
דוגמה - מחלקת 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
דוגמה - מחלקת 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
דוגמה - מחלקת 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
העמסת אופרטורים - הערות 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
העמסת אופרטורים • לאופרטורים ++ ו--- יש שתי גרסאות: pre ו-post • כדי להגדיר++i מגדירים את האופרטור כרגיל • כדי להגדיר את i++מוספים פרמטר דמה • שימוש בהעמסת אופרטורים כדי "להמציא שפה" פוגע בקריאות הקוד • list.add(item)יותר ברור מ-list += item • שימוש באופרטורים מתאים רק עבור מקרים בהם קיימת שפה משותפת ידועה (למשל אופרטור * עבור כפל מטריצות) classX{ //... public: X& operator++(); // ++x Xoperator++(int);// x++ }; מדוע ערך ההחזרה שונה? מבוא לתכנות מערכות - 234122
העמסת אופרטורים - סיכום • ניתן להתייחס לאופרטורים כפונקציות רגילות ב-C++ • ניתן להעמיס על אופרטורים קיימים כך שישמשו עבור מחלקות • את האופרטורים ניתן להגדיר כמתודות (אשר הפרמטר הראשון שלהן הוא this) וכפונקציות מחוץ למחלקות • לחלק מהאופרטורים יש הגבלות מיוחדות • מומלץ להימנע מהעמסת אופרטורים כל עוד היא לא אינטואיטיבית מבוא לתכנות מערכות - 234122
בנאים, הורסים והשמות בנאים בנאי העתקה הורסים אופרטור ההשמה מבוא לתכנות מערכות - 234122
בנאים והורסים - C’tors & D’tors • השימוש בפונקציות מפורשות לאתחול ושחרור עצמים (כמו ב-C) חשוף לטעויות: • המשתמש יכול לשכוח לאתחל/לשחרר את העצם • המשתמש יכול לאתחל/לשחרר את העצם פעמיים • לשם כך נוספה ב-C++ האפשרות להגדיר פונקציות ייעודיות לאתחול ושחרור עצמים - בנאים(Constructors) והורסים(Destructors) • כל עצם שנוצר ב-C++ מאותחל על ידי בנאי וכל עצם שמשוחרר מטופל על ידי הורס מבוא לתכנות מערכות - 234122
בנאים - 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
רשימת אתחול אתחול 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
בנאי העתקה - 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
בנאי העתקה - 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
הורסים - 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
זמני קריאה • רשימת האתחול של בנאי מבוצעת לפני הכניסה לקוד הבנאי עצמו (בתוך ה-{ }) • סדר הקריאה לאתחול השדות הוא לפי סדר הגדרתם במחלקה • לאחר ביצוע הקוד שבתוך ה-{ } בהורס ייקראו הורסים לכל שדותיו של העצם הנהרס (בסדר ההפוך מבאתחול העצם) 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
אופרטור השמה • השמה היא הפעולה של שינוי ערכיו של משתנה קיים כך שיהיו זהים לשל משתנה אחר • ניתן להעמיס על אופרטור ההשמה שחתימתו היא: X& X::operator=(constX& x) • ניתן להעמיס על אופרטור= גם עם חתימות אחרות, אך בדרך כלל אין בכך צורך • אם לא נכתב אופרטור השמה הקומפיילר ייצור אחד בעצמו • אופרטור ההשמה שהקומפיילר יוצר קורא לאופרטורי השמה של השדות מבוא לתכנות מערכות - 234122
אופרטור השמה • אופרטור השמה למחסנית 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
פונקציות הנוצרות על ידי הקומפיילר • הקומפיילר יוצר את הפונקציות הבאות בעצמו: • בנאי חסר פרמטרים: מאתחל את כל השדות של העצם בעזרת בנאי חסר פרמטרים • אם נכתב בנאי כלשהובמפורש הקומפיילר לא ייצור את פונקציה זו • ב C++ 11 ניתן לייצר בנאי זה ע"י שימוש ב =default (אחרי הגדרה של בנאי ללא ארגומנטים). • בנאי העתקה: מאתחל את כל שדות העצם בעזרת בנאי ההעתקה שלהם (והשדה המתאים בעצם אותו מעתיקים) • הקומפיילר ייצור את בנאי ההעתקה גם אם הוגדר בנאי אחר! • אופרטור השמה: קורא לאופרטור ההשמה של כל השדות של העצם • הורס: קורא להורסים של כל השדות של העצם • אין צורך לממש פונקציות שהקומפיילר יוצר בעצמו ומתאימות לנו מבוא לתכנות מערכות - 234122
Big three • בין בנאי ההעתקה, ההורס ואופרטור ההשמה קיים קשר:אם צריך אחד מהם, צריך את שלושתם • איך יודעים מתי צריך לממש את הפונקציות האלו? • אם מעורבים מצביעים במחלקה בדרך כלל יש צורך בפונקציות האלו • אם יש שימוש ב-new ו-delete • אם מתבצעות הקצאות של משאבים אחרים (למשל קבצים) מבוא לתכנות מערכות - 234122
בנאים, הורסים והשמות - סיכום • בנאים משמשים לאתחול עצמים • לפני הכניסה לקוד הבנאי נקראים הבנאים של כל השדות של העצם • אחרי ביצוע הקוד בהורס נקראים ההורסים של כל שדות העצם • בנאי ההעתקה משמש להעברת והחזרת ערכים by value ואתחול העתקים • אופרטור ההשמה (=) משמש להעתקת ערכים בין שני עצמים קיימים • בכתיבת אופרטור השמה חשוב לשים לב לשחרור המידע הקיים והשמות עצמיות • הקומפיילר יכתוב בעצמו בנאי חסר פרמטרים, בנאי העתקה, אופרטור השמה והורס. אין צורך לכתוב את הפונקציות האלו אם המימוש של הקומייפלר מתאים • אם צריך בנאי העתקה, אופרטור השמה או הורס לא טריוויאלים אז צריך את שלושתם מבוא לתכנות מערכות - 234122
המרות אוטומטיות המרות על ידי בנאי העמסת אופרטור ההמרה בנאים מפורשים מבוא לתכנות מערכות - 234122
המרות • לפעמים נרצה מספר פעולות בין טיפוסים שונים • למשל, פעולות חיבור מספר מרוכב עם ממשי Complexoperator+(constComplex&, constComplex&); Complexoperator+(constdouble&, constComplex&); Complexoperator+(constComplex&, constdouble&); • לא נרצה לממש מספר פונקציות למטרה זו • ניתן להגדיר המרות בין טיפוסים. כך למשל הקומפיילר יוכל להמיר double ל-Complex ונצטרך רק פונקציה אחת עבור החיבור Complexoperator+(constComplex&, constComplex&); • קיימות שתי דרכים להגדיר המרות ב-C++: • על ידי בנאים • על ידיד העמסת אופרטור המרה מבוא לתכנות מערכות - 234122
המרה על ידי בנאי 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
העמסת אופרטור המרה • השימוש בבנאי עבור המרה אינו מאפשר לנו להמיר עצם ממחלקה לערך בסיסי או להמיר עצם ממחלקה חדשה למחלקה ישנה • ניתן להעמיס על אופרטור המרה - שם האופרטור כשם הטיפוס אליו נרצה להמיר • את האופרטור יש להגדיר כמתודה • האופרטור מוגדר ללא ערך חזרה, טיפוס ערך החזרה נקבע לפי שם האופרטור 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
המרות אוטומטיות • מומלץ להימנע מהגדרת המרות אוטומטיות ולהעדיף הגדרת פונקציות מפורשות לביצוע המרות • המרות אוטומטיות עלולות לגרום לדו-משמעות • המרות אוטומטיות עלולות לגרום לקוד מוזר להתקמפל 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
המרות אוטומטיות • ניתן להוסיף את המילה השמורה 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
המרות אוטומטיות • מתודותניתן להפעיל על עצם מהטיפוס המתאים בלבד • הקומפיילר לא ימיר משתנה כדי להפעיל עליו מתודה classComplex { doublere, im; public: // ... Complex(double re); doubleabs() const; }; voidf(double& d) { d.abs(); // error Complex(d).abs(); // o.k. } מבוא לתכנות מערכות - 234122
המרות אוטומטיות - סיכום • בנאים המקבלים פרמטר אחד ישמשו את הקומפיילר לביצוע המרות אוטומטיות • ניתן להעמיס על אופרטור המרה וכך להגדיר המרה מטיפוס שיצרנו לטיפוס שלא כתבנו בעצמנו • מומלץ להימנע מהגדרת המרות אוטומטיות מאחר והן יכולות לגרום לקוד פחות קריא, בעיות דו-משמעות, וקמפול מוצלח של קוד לא הגיוני • ניתן למנוע מהקומפיילר להשתמש בבנאי עבור המרות על ידי הוספת המילה explicit בהכרזה עליו • הקומפיילר לא ישתמש בהמרות כדי להפעיל מתודות מבוא לתכנות מערכות - 234122
חברים - friends פונקציות חברות מחלקות חברות העמסת אופרטורי קלט ופלט מבוא לתכנות מערכות - 234122
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
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
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
העמסת אופרטור הפלט • אופרטור הפלט >> מועמס כדי לאפשר הדפסה נוחה של עצמים • לא נוכל להגדיר את אופרטור >> כמתודה של מחלקה שכתבנו, מדוע? • נגדיר את אופרטור >> כפונקציה חיצונית: • במקרים רבים נצטרך גישה לשדות פרטיים - במקרים אלו נכריז על האופרטור כ-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
מחלקת 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
מחלקת 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
מחלקת 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
מחלקת 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
friend - סיכום • ניתן להכריז על פונקציה שאינה שייכת למחלקה כחברה שלה ולאפשר לה גישה לשדות פרטיים • ניתן להכריז על מחלקה כחברה של מחלקה אחרת כך שכל המתודות שלה ייחשבו כחברות • משתמשים ב-friend עבור העמסת אופרטורים שצריכים להיות מוכרזים כפונקציות חיצוניות (רק אם אכן יש צורך בכך) מבוא לתכנות מערכות - 234122