43
ססססס סס'8 םםםםם םםםםםםםםם םםםםם םםםםםם םםםםםם םםםםם friend

תרגול מס ' 8

  • Upload
    uri

  • View
    78

  • Download
    2

Embed Size (px)

DESCRIPTION

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

Citation preview

Page 1: תרגול מס ' 8

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

Page 2: תרגול מס ' 8

2מבוא לתכנות מערכות - 234122

העמסת אופרטורים

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

Page 3: תרגול מס ' 8

3מבוא לתכנות מערכות - 234122

העמסת אופרטורים מבחינתC ++כמו אופרטורים+

הם פונקציות רגילות*או

ניתן להעמיס על אופרטוריםבדומה לפונקציות רגילות עבור

מחלקות

ניתן לקרוא לכל אופרטור מועמסבשתי דרכים

בעזרת התחביר המיוחד השמור –לו, למשל עבור אופרטור+

a+bבעזרת בעזרת תחביר של פונקציה –

, <operator<opרגילה ששמה operator+(a,b)למשל

class Complex { double re, im;

public:Complex(double r, double i);Complex operator+(const Complex& c)

const;Complex operator*(const Complex& c)

const;};

int main() {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ה-

Page 4: תרגול מס ' 8

4מבוא לתכנות מערכות - 234122

העמסת אופרטורים על האופרטורים הבאים:ניתן להעמיס

+ - * / % ^ &

| ~ ! = < > +=

-= *= /= %= ^= &= |=

<< >> >>= <<= == != <=

>= && || ++ -- ->* ,

-> [] () new new[] delete delete[]

על האופרטורים הבאים:לא ניתן להעמיס

:: . .* ? : sizeof typeid

)** לא ניתן להגדיר אופרטורים חדשים )למשלשלהם נשמריםהאסוציאטיביות וקדימותם, מספר הפרמטרים אחד הפרמטרים לאופרטור חייב להיות מטיפוס מחלקה אוenum

Page 5: תרגול מס ' 8

5מבוא לתכנות מערכות - 234122

העמסת אופרטורים כפונקציות חיצוניות או כמתודותניתן להגדיר את האופרטורים

ויש thisהפרמטר הראשון הוא ה-אם אופרטור המקבל פרמטרים מוגדר כמתודה – פרמטרים נוספיםלהכריז על

הפרמטריםאם האופרטור מוגדר כפונקציה חיצונית יש להכריז על כל –

* לחלק מהאופרטורים קיימות גרסה אונארית וגם גרסה בינארית, למשל - או

class Complex {

double re, im;

public:

Complex(double r, double i);

Complex operator+(const Complex& c) const;

Complex operator-() const; // c1 = -c2;

};

 

Complex operator-(const Complex& c1, const Complex& c2);

Page 6: תרגול מס ' 8

6מבוא לתכנות מערכות - 234122

העמסת אופרטורים-תאימות ל( בגלל סיבות היסטוריותC לכל מחלקה מוגדרים האופרטורים הבאים )

על ידי הקומפיילר: = )השמה(, & )כתובת של( ו-, )סדרתיות( ניתן להעמיס את האופרטורים האלה מחדש או למנוע מהמשתמש במחלקה

גישה מהם על ידי הכרזתם כפרטיים.class X {

private:

void operator=(const X&);

void operator&();

void operator,(const X&);

//...

};

בC++ 11:ניתן למנוע גישה גם ע"י

class X {

void operator=(const X&) = delete;

//...

};

Page 7: תרגול מס ' 8

7מבוא לתכנות מערכות - 234122

Complexדוגמה - מחלקת class Complex {

double re, im;

public:

Complex(double r, double i);

Complex& operator+=(const Complex& c);

Complex& operator-=(const Complex& c);

Complex operator-() const;

bool operator==(const Complex& c) const;

};

 

Complex operator+(const Complex& a, const Complex& b);

Complex operator-(const Complex& a, const Complex& b);

 

אופרטורים הכוללים השמה += מאפשרים שרשור ולכן כמו

נשמור על ההתנהגות הזו אצלנו

אופרטור - אונארי יוצר מספר מרוכב חדש, לכן התוצאה

מוחזרת כעותק חדשאופרטורים סימטריים נהוג

להכריז מחוץ למחלקה בגלל המרות )דוגמה בהמשך

התרגול(

Page 8: תרגול מס ' 8

8מבוא לתכנות מערכות - 234122

Complexדוגמה - מחלקת Complex& Complex::operator+=(const Complex& c) {

re += c.re;im += c.im;return *this;

} Complex& Complex::operator-=(const Complex& c) {

return this->operator+=(-c); // or *this += -c} Complex& Complex::operator-() const {

return *(new Complex(-re, -im));} Complex operator+(const Complex& a, const Complex& b) {

Complex c = a;return c += b;

}

Page 9: תרגול מס ' 8

9מבוא לתכנות מערכות - 234122

Complexדוגמה - מחלקת bool Complex::operator==(const Complex& c) const {

return c.re == re && c.im == im;

}

 

int main() {

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;

}

 

 

Page 10: תרגול מס ' 8

10מבוא לתכנות מערכות - 234122

העמסת אופרטורים - הערות

][ אופרטור(subscripting יכול )לשמש להגדרת אוספים

חייב להיות מוגדר בתוך המחלקה–

(intמקבל פרמטר נוסף )לא בהכרח –

)( אופרטור(function call יכול )להיות מוגדר לכל מספר של פרמטרים

חייב להיות מוגדר בתוך המחלקה–

-דוגמאות לשימוש ב)(:החזרת איבר ממטריצה–

החזרת תת-טווח של אוסף–

עצמים המתנהגים כפונקציות –(12)דוגמאות לכך בתרגול

class Random {int seed;

public:Random(int seed);int operator()(int max);

};//...Random r(5);int random = r(100);

class Array {int* array;int size;

public://...int& operator[](int index);

};

int& Array::operator[](int index) {assert(index >= 0 && index

< size);return array[index];

}

Page 11: תרגול מס ' 8

11מבוא לתכנות מערכות - 234122

העמסת אופרטורים

יש שתי גרסאות: -- ו-++לאופרטורים pre-ו post כדי להגדיר++iמגדירים את האופרטור כרגיל כדי להגדיר אתi ++מוספים פרמטר דמה

פוגע בקריאות הקוד"להמציא שפה" שימוש בהעמסת אופרטורים כדי–list.add(item) -יותר ברור מ list += item

שפה משותפת שימוש באופרטורים מתאים רק עבור מקרים בהם קיימת)למשל אופרטור * עבור כפל מטריצות(ידועה

class X {

//...

public:

X& operator++(); // ++x

X operator++(int); // x++

};

מדוע ערך ההחזרה שונה?

Page 12: תרגול מס ' 8

12מבוא לתכנות מערכות - 234122

העמסת אופרטורים - סיכום

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

( וכפונקציות מחוץ למחלקותthisהוא לחלק מהאופרטורים יש הגבלות מיוחדותמומלץ להימנע מהעמסת אופרטורים כל עוד היא לא אינטואיטיבית

Page 13: תרגול מס ' 8

13מבוא לתכנות מערכות - 234122

בנאים, הורסים והשמות

בנאיםבנאי העתקההורסיםאופרטור ההשמה

Page 14: תרגול מס ' 8

14מבוא לתכנות מערכות - 234122

C’tors & D’torsבנאים והורסים - -כמו ב( השימוש בפונקציות מפורשות לאתחול ושחרור עצמיםC חשוף )

לטעויות:את העצםלשכוח לאתחל/לשחרר המשתמש יכול –

פעמייםהמשתמש יכול לאתחל/לשחרר את העצם –

-לשם כך נוספה בC האפשרות להגדיר פונקציות ייעודיות לאתחול ++(Destructors )והורסים( Constructors )בנאיםושחרור עצמים -

-כל עצם שנוצר בC מאותחל על ידי בנאי וכל עצם שמשוחרר מטופל ++על ידי הורס

Page 15: תרגול מס ' 8

15מבוא לתכנות מערכות - 234122

Constructorsבנאים - בנאי מוגדר על ידי מתודה ששמה כשם המחלקהלבנאי אין ערך החזרה ניתן להגדיר מספר בנאים בהתאם לחוקי העמסת

פונקציות לבנאי שאינו מקבל פרמטרים קוראים גםdefault

c’tor אם לא מוגדר בנאי בצורה מפורשת למחלקה

הקומפיילר ייצור בנאי חסר פרמטרים בעצמוהבנאי הנוצר על ידי הקומפיילר קורא לבנאי חסר –

הפרמטרים של כל אחד מהשדות שלו

אם מוגדר בנאי כלשהו הקומפיילר לא ייצור בנאי–

באתחול של מערך נקרא בנאי חסר פרמטרים לכלאחד מהאיברים

לא ניתן ליצור מערך של עצמים שאין להם בנאי מתאים–

class X {public:

X();X(int n);

};

X global;

int main() {X x1;X x2(5);X* ptrx1 = new

X;X* ptrx2 = new

X();X* arrayx = new

X[10];arrayx[0] =

X();arrayx[1] =

X(5);// ...return 0;

}

איזה בנאי נקרא בכל אחת מהשורות?

Page 16: תרגול מס ' 8

16מבוא לתכנות מערכות - 234122

רשימת אתחול

-חשוב להבדיל בC:בין אתחול להשמה ++אתחול מתבצע על ידי בנאי–

השמה מתבצעת על ידי אופרטור=–

?מה הבעיה בקוד הזה

ברשימת הבנאי אחראי לאתחול השדות של המחלקה, אתחול השדות מתבצע לפני הכניסה לגוף הפונקציה על ידי קריאה לבנאיםהאתחול

אם עבור שדה מסוים לא מצוין כיצד יש לאתחל אותו הוא יאותחל על ידי בנאיחסר פרמטרים

תתקבל שגיאת קומפילציהאם אין בנאי מתאים לשדה –

Complex::Complex(double r, double i) :

re(r), im(i) {

}

 

Complex::Complex(double r, double i) {

re = r;im = i;

}

int n(5);

int n = 5;

n = 5;

אתחול

אתחול

השמה

re)(-ו im )(נקראים כאן

Page 17: תרגול מס ' 8

17מבוא לתכנות מערכות - 234122

Copy C’torבנאי העתקה - בנאי לבנאי המקבל פרמטר מטיפוס העצם אותו הוא מאתחל קוראים

( ויש לו מעמד מיוחדCopy C’tor)העתקה X::X(const X& x)חתימת בנאי ההעתקה היא: –

בנאי ההעתקה משמש להעברת פרמטרים והחזרת ערכיםby value:

 אם לא מוגדר בנאי העתקה הקומפיילר ייצור אחד בעצמובנאי ההעתקה שנוצר על ידי הקומפיילר מעתיק את איברי המחלקה אחד אחד –

בעזרת בנאי ההעתקה המתאים של כל שדה

שימו לב: בנאי העתקה נוצר על ידי הקומפיילר גם אם הוגדרו בנאים אחרים–

Y f(X x, Y& y) { // X::X(const X&)// ...return y; // Y::Y(const Y&)

}

byלמה הפרמטר מועבר reference?

Page 18: תרגול מס ' 8

18מבוא לתכנות מערכות - 234122

Copy C’torבנאי העתקה - בנאי ההעתקה הנוצר אוטומטית עבורStack:ייראה כך ?מדוע הוא לא מתאים לנו

:בנאי העתקה מתאים ייראה כך

Stack::Stack(const Stack& s) :data(s.data),size(s.size),nextIndex(s.nextIndex) {

}

Stack::Stack(const Stack& s) :data(new int[s.size]),size(s.size),nextIndex(s.nextIndex) {for(int i = 0; i <

nextIndex; i++) {data[i] = s.data[i];

}}

Page 19: תרגול מס ' 8

19מבוא לתכנות מערכות - 234122

Destructorsהורסים - ההורס של מחלקהX מוגדר כמתודה ששמה

~X הורס אינו מקבל פרמטרים ואין לו ערך

חזרה הורס יחידניתן להגדירכאשר עצם משוחרר נקרא ההורס שלו

ההורס נקרא אוטומטית - לא כותבים קריאה –מפורשת להורס

אם לא מוגדר הורס הקומפיילר יגדיר אחדבעצמו

גוף הפונקציה של ההורס יהיה ריק–

של עצם כלשהו ייקראו לאחר סיום ההורסההורסים של כל השדות שלו

X global; int main() {

{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;}

 מתי נקראים הורסים בקוד הזה?

Page 20: תרגול מס ' 8

20מבוא לתכנות מערכות - 234122

זמני קריאה

של בנאי רשימת האתחוללפני הכניסה לקוד מבוצעת עצמו )בתוך ה-} {(הבנאי

סדר הקריאה לאתחול השדות –הוא לפי סדר הגדרתם

במחלקה

שבתוך לאחר ביצוע הקודייקראו הורסים ה-} { בהורס של העצם הנהרס לכל שדותיו

)בסדר ההפוך מבאתחול העצם(

class X {int n;

public:X(int n) : n(n) { cout << "X::X():" << n <<

endl; }~X() { cout << "X::~X():" << n

<< endl; }};

class Y {X x1, x2;

public:Y() : x1(1), x2(2)

{ cout << "Y::Y()" << endl; }

~Y() { cout << "Y::~Y()" << endl; }};

int main() {Y y;return 0;

}

מה יודפס?

Page 21: תרגול מס ' 8

21מבוא לתכנות מערכות - 234122

אופרטור השמה

כך שיהיו זהים משתנה קיים השמה היא הפעולה של שינוי ערכיו שללשל משתנה אחר

שחתימתו היא:ניתן להעמיס על אופרטור ההשמה

X& X::operator=(const X& x)

ניתן להעמיס על אופרטור= גם עם חתימות אחרות, אך בדרך כלל איןבכך צורך

אם לא נכתב אופרטור השמה הקומפיילר ייצור אחד בעצמואופרטור ההשמה שהקומפיילר יוצר קורא לאופרטורי השמה של השדות–

Page 22: תרגול מס ' 8

22מבוא לתכנות מערכות - 234122

אופרטור השמה

אופרטור השמה למחסניתStack& Stack::operator=(const Stack& s) {

if (this == &s) {

return *this;

}

delete[] data;

data = new int[s.size];

size = s.size;

nextIndex = s.nextIndex;

for(int i=0; i < nextIndex; i++) {

data[i] = s.data[i];

}

return *this;

}

בדיקת השמה עצמיתמדוע בדיקה זו

הכרחית?מחיקת מידע ישן, בניגוד לבנאי העתקה צריך לנקות את המידע

הקודם

ביצוע הקצאות חדשות

עבור שרשורthis*החזרת

העברת המידע

מימוש זה לא מתאים עבור מקרה של חריגות )שיילמדו בהמשך(,

נראה מימוש טוב יותר לאופרטור= בעתיד

Page 23: תרגול מס ' 8

23מבוא לתכנות מערכות - 234122

פונקציות הנוצרות על ידי הקומפיילר

:הקומפיילר יוצר את הפונקציות הבאות בעצמו: מאתחל את כל השדות של העצם בעזרת בנאי חסר בנאי חסר פרמטרים–

פרמטרים

במפורש הקומפיילר לא ייצור את פונקציה זוכלשהואם נכתב בנאי •

)אחרי הגדרה default ניתן לייצר בנאי זה ע"י שימוש ב =C++ 11ב –של בנאי ללא ארגומנטים(.

: מאתחל את כל שדות העצם בעזרת בנאי ההעתקה שלהם בנאי העתקה–)והשדה המתאים בעצם אותו מעתיקים(

הקומפיילר ייצור את בנאי ההעתקה גם אם הוגדר בנאי אחר!•

: קורא לאופרטור ההשמה של כל השדות של העצםאופרטור השמה–

: קורא להורסים של כל השדות של העצםהורס–

לנוומתאימות פונקציות שהקומפיילר יוצר בעצמו אין צורך לממש

Page 24: תרגול מס ' 8

24מבוא לתכנות מערכות - 234122

Big three:בין בנאי ההעתקה, ההורס ואופרטור ההשמה קיים קשר

אם צריך אחד מהם, צריך את שלושתם

?איך יודעים מתי צריך לממש את הפונקציות האלואם מעורבים מצביעים במחלקה בדרך כלל יש צורך בפונקציות האלו–

delete ו-newאם יש שימוש ב-–אם מתבצעות הקצאות של משאבים אחרים )למשל קבצים(–

Page 25: תרגול מס ' 8

25מבוא לתכנות מערכות - 234122

בנאים, הורסים והשמות - סיכום

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

עצמיות הקומפיילר יכתוב בעצמו בנאי חסר פרמטרים, בנאי העתקה, אופרטור

השמה והורס. אין צורך לכתוב את הפונקציות האלו אם המימוש של הקומייפלר מתאים

אם צריך בנאי העתקה, אופרטור השמה או הורס לא טריוויאלים אז צריךאת שלושתם

Page 26: תרגול מס ' 8

26מבוא לתכנות מערכות - 234122

המרות אוטומטיות

המרות על ידי בנאיהעמסת אופרטור ההמרהבנאים מפורשים

Page 27: תרגול מס ' 8

27מבוא לתכנות מערכות - 234122

המרותלפעמים נרצה מספר פעולות בין טיפוסים שונים

למשל, פעולות חיבור מספר מרוכב עם ממשי–

Complex operator+(const Complex&, const Complex&);

Complex operator+(const double&, const Complex&);

Complex operator+(const Complex&, const double&);

לא נרצה לממש מספר פונקציות למטרה זו–

ניתן להגדיר המרות בין טיפוסים. כך למשל הקומפיילר יוכל להמירdouble-ל Complex ונצטרך רק פונקציה אחת עבור החיבור

Complex operator+(const Complex&, const Complex&);

-קיימות שתי דרכים להגדיר המרות בC:++בנאיםעל ידי –

העמסת אופרטור המרהעל ידיד –

Page 28: תרגול מס ' 8

28מבוא לתכנות מערכות - 234122

המרה על ידי בנאי המקבל פרמטר אם למחלקה יש בנאי

אז בנאי זה ישמש את הקומפיילר יחידלביצוע המרות אוטומטיות

המרות המוגדרות על ידי המשתמשלבחירה בין ישמשו את הקומפיילר

פונקציות מועמסותהמרות אלו מקבלות עדיפות נמוכה יותר –

מהמרות מובנות

בנאי עם ערכי ברירת מחדל היכול לקבלארגומנט יחיד גם יתאים להמרות,

למשל:Complex(double re = 0.0,double im = 0.0)

: re(re), im(im) {}

class Complex {double re, im;

public:Complex(double re) :

re(re), im(0.0) {}//...

}; Complex operator+(const Complex&, const Complex&);int main() {

Complex c = 3.0;Complex c2(1.0,1.0);c = 4.0;c = c2 + 2.0;return 0;

}

Page 29: תרגול מס ' 8

29מבוא לתכנות מערכות - 234122

העמסת אופרטור המרה

השימוש בבנאי עבור המרה אינוממחלקה מאפשר לנו להמיר עצם

או להמיר עצם לערך בסיסיממחלקה חדשה למחלקה ישנה

אופרטור המרה ניתן להעמיס על- שם האופרטור כשם הטיפוס אליו

נרצה להמירכמתודהאת האופרטור יש להגדיר –

, ללא ערך חזרההאופרטור מוגדר –טיפוס ערך החזרה נקבע לפי שם

האופרטור

class Rational {int num, denom;

public:Rational(int num, int

denom);// ...operator double() const;

}; Rational::operator double() const {

return double(num)/denom;} int main() {

Rational r(1,2);double d = 0.3;cout << (d+r) << endl;return 0;

}

Page 30: תרגול מס ' 8

30מבוא לתכנות מערכות - 234122

המרות אוטומטיות ולהעדיף הגדרת פונקציות להימנע מהגדרת המרות אוטומטיות מומלץ

מפורשות לביצוע המרותהמרות אוטומטיות עלולות לגרום לדו-משמעות–

המרות אוטומטיות עלולות לגרום לקוד מוזר להתקמפל–

class Rational {//...Rational(int num, int denom=1);operator double() const;

};

Rational operator+(const Rational& a, const Rational& b);

void f(const Rational& r, int n) {r + n; // error, ambiguous

}

class Array {//...

public:Array(int size);

}; void f(Array& array) {

array = 50; // Compiles}

Page 31: תרגול מס ' 8

31מבוא לתכנות מערכות - 234122

המרות אוטומטיות ניתן להוסיף את המילה השמורהexplicit בתחילת בנאי כדי לציין שבנאי

זה לא ישמש להמרה לא מפורשתארגומנטמומלץ להכריז על בנאים המקבלים –

explicit כ-יחידבמיוחד אם הארגומנט הוא מטיפוס–

(intבסיסי )למשל

שתי המרות אוטומטיות על אותו ארגומנט, הקומפיילר לא יבצעבמקרים בהם זה דרוש יש להמיר בצורה מפורשת

Complex שיומר ל-double לא יומר ל-Rationalלמשל –

class Array {//...explicit Array(int

size);};

void f(Array& array) {array = 50; //

error}

void f(const Rational& r, const Complex& c) {Complex c3 = c + r; // errorComplex c2 = c + double(r); // O.K. double -

> Complex}

Page 32: תרגול מס ' 8

32מבוא לתכנות מערכות - 234122

המרות אוטומטיות

ניתן להפעיל על עצם מהטיפוס המתאים בלבדמתודות הקומפיילר לא ימיר משתנה כדי להפעיל עליו מתודה

class Complex {

double re, im;

public:

// ...

Complex(double re);

double abs() const;

};

 

void f(double& d) {

d.abs(); // error

Complex(d).abs(); // o.k.

}

Page 33: תרגול מס ' 8

33מבוא לתכנות מערכות - 234122

המרות אוטומטיות - סיכום

בנאים המקבלים פרמטר אחד ישמשו את הקומפיילר לביצוע המרותאוטומטיות

ניתן להעמיס על אופרטור המרה וכך להגדיר המרה מטיפוס שיצרנולטיפוס שלא כתבנו בעצמנו

מומלץ להימנע מהגדרת המרות אוטומטיות מאחר והן יכולות לגרוםלקוד פחות קריא, בעיות דו-משמעות, וקמפול מוצלח של קוד לא הגיוני

ניתן למנוע מהקומפיילר להשתמש בבנאי עבור המרות על ידי הוספת בהכרזה עליוexplicitהמילה

הקומפיילר לא ישתמש בהמרות כדי להפעיל מתודות

Page 34: תרגול מס ' 8

34מבוא לתכנות מערכות - 234122

friendsחברים -

פונקציות חברותמחלקות חברותהעמסת אופרטורי קלט ופלט

Page 35: תרגול מס ' 8

35מבוא לתכנות מערכות - 234122

friendפונקציות חברות - לעתים נצטרך לאפשר לפונקציה חיצונית

לגשת לשדות פרטיים

כדי לאפשר לפונקציה לגשת לחלקיםפרטיים של מחלקה מסוימת נכריז עליה

friendבתוך המחלקה אחרי המילה השמורה

הקוד שמופיע בגוף הפונקציה שהוכרזהשל רשאי לגשת לחלקים פרטיים כחברה

friendהמחלקה בה הוכרז כ-

ניתן לממש את הפונקציה יחד עם ההכרזהאו בחוץ כרגיל

class A {int n;

public:A(int n) { this->n =

n; }friend int getN(const

A& a);}; int getN(const A& a) {

return a.n;} int main() {

A a(5);cout << getN(a) <<

endl;return 0;

}

Page 36: תרגול מס ' 8

36מבוא לתכנות מערכות - 234122

friendמחלקות חברות - -ניתן להכריז על מחלקה שלמה כfriend

-קוד אשר נכתב בscope של המחלקה B רשאי לגשת לחלקים Aשהוכרזה כחברה של

Aפרטיים של Aלא ניתן לבצע את ההיפך - לגשת מ-–

Bלחלקים פרטיים של

-מומלץ להימנע משימוש בfriend בדרך -של המערכת תכן רע כלל זהו תסמין של

בין החלקים השונים בקודעודף תלויותו

עם זאת קיימים מקרים בהם השימוש חשובfriendב-

class A {int n;

public:A(int n) { this->n =

n; }friend class B;

};

class B {public:

void printA(const A& a) {

cout << a.n << endl;

}};

int main() {A a(5);B b;b.printA(a);return 0;

Page 37: תרגול מס ' 8

37מבוא לתכנות מערכות - 234122

friendsוהעמסת אופרטורים

מה הבעיה בפונקציהfכאשר אופרטור == ממומש כמתודה?

התנהגותכדי לאפשרשל האופרטורסימטרית

עלינו להגדיר אותוכפונקציה חיצונית

עבורלאפשר גישה לשדות כדיהפונקציה החדשה נצטרך להכריז

friendעליה כ-

class Complex {//...bool operator==(const Complex&)

const;};

bool Complex::operator==(const Complex& c) const {

return c.re == re && c.im == im;}class Complex {

//...bool operator==(const

Complex&, const

Complex&);};

bool operator==(const Complex& a, const Complex& b) {

return a.re == b.re && a.im == b.im;}

void f(const double& d, const Complex& c) {

cout << (c == d) << (d == c) << endl;}

class Complex {//...friend bool operator==(const

Complex&, const

Complex&);};

bool operator==(const Complex& a, const Complex& b) {

return a.re == b.re && a.im == b.im;}

Page 38: תרגול מס ' 8

38מבוא לתכנות מערכות - 234122

העמסת אופרטור הפלט

אופרטור הפלט << מועמס כדי לאפשר הדפסה נוחה של עצמים?לא נוכל להגדיר את אופרטור << כמתודה של מחלקה שכתבנו, מדוע:נגדיר את אופרטור << כפונקציה חיצונית

במקרים רבים נצטרך גישה לשדות פרטיים - במקרים אלו נכריז עלfriendהאופרטור כ-

את אופרטור הקלט מעמיסים בצורה דומה עבור המחלקהistream

ostream& operator<<(ostream& os, const Complex& c) {

const char* sign = c.im < 0 ? "" : "+";return os << c.re << sign << c.im << "i";

}

class Complex {//...friend ostream& operator<<(ostream& os, const

Complex& c);};

Page 39: תרגול מס ' 8

39מבוא לתכנות מערכות - 234122

המעודכנתComplexמחלקת class Complex {

double re, im;

public:

Complex(double re = 0.0, double im = 0.0);

Complex& operator+=(const Complex& c);

Complex& operator-=(const Complex& c);

Complex operator-() const;

friend bool operator==(const Complex& a, const Complex& b);

friend ostream& operator<<(ostream& os, const Complex& c);

friend istream& operator>>(istream& is, Complex& c);

};

 

Complex operator+(const Complex& a, const Complex& b);

Complex operator-(const Complex& a, const Complex& b);

מאפשר המרות ואתחול מערכים

עבור אופרטורים אלו אין צורך friendב-

איפה בנאי ההעתקה, ההורס ואופרטור ההשמה? למה?

Page 40: תרגול מס ' 8

40מבוא לתכנות מערכות - 234122

המעודכנתComplexמחלקת Complex::Complex(double re, double im) : re(re), im(im) {}

 

Complex& Complex::operator+=(const Complex& c) {

re += c.re;

im += c.im;

return *this;

}

 

Complex& Complex::operator-=(const Complex& c) {

return *this += -c;

}

 

Complex Complex::operator-() const {

return Complex(-re, -im);

}

Page 41: תרגול מס ' 8

41מבוא לתכנות מערכות - 234122

המעודכנתComplexמחלקת Complex operator+(const Complex& a, const Complex& b) {

return Complex(a) += b;

}

 

Complex operator-(const Complex& a, const Complex& b) {

return Complex(a) -= b;

}

 

ostream& operator<<(ostream& os, const Complex& c) {

const char* sign = c.im < 0 ? "" : "+";

return os << c.re << sign << c.im << "i";

}

 

istream& operator>>(istream& is, Complex& c) {

return is >> c.re >> c.im;

}

?constלמה אין

כדי לאפשר osמחזירים את שרשור

Page 42: תרגול מס ' 8

42מבוא לתכנות מערכות - 234122

המעודכנתComplexמחלקת bool operator==(const Complex& a, const Complex& b) {

return a.re == b.re && a.im == b.im;

}

 

int main() {

Complex a = 1.0;

Complex b;

cin >> b;

cout << (-a+b) << endl;

return 0;

}

 

Page 43: תרגול מס ' 8

43מבוא לתכנות מערכות - 234122

friendסיכום -

ניתן להכריז על פונקציה שאינה שייכת למחלקה כחברה שלה ולאפשרלה גישה לשדות פרטיים

ניתן להכריז על מחלקה כחברה של מחלקה אחרת כך שכל המתודותשלה ייחשבו כחברות

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