C++/העמסת אופרטורים: הבדלים בין גרסאות בדף

תוכן שנמחק תוכן שנוסף
דף חדש: {{בעבודה}} כל ביטוי מורכב מפעולות ופרמטרים איתם מתבצעות הפעולות, פעולות אלו נקראות '''אופרטורים''' ואילו ...
 
Ybungalobill (שיחה | תרומות)
אין תקציר עריכה
שורה 1:
{{C++}}
{{בעבודה}}
כל ביטוי מורכב מפעולות ופרמטרים איתם מתבצעות הפעולות, פעולות אלו נקראות '''אופרטורים''' ואילו הפרמטרים איתם עובדים האופרטורים נקראים '''אופרנדים'''. בC++ ישנם אופרטורים רבים שחלקם מקבלים כפרמטרים טיפוסים ומרחבי שמות (כמו :: ו-sizeof), פעולתם מתבצעת רק בעת הידור התוכנית, ואילו האחרים מקבלים אובייקטים, אופרטורים אלה עובדים עם האוביקטיים בזמן ריצה. כאשר אנו מגדירים מחלקות אנו מוסיפים טיפוסים חדשים לשפה, אבל עד כה ביצענו איתם פעולות רק ע"י קריאות לפונקציות המחלקה או פונקציות רגילות. כדי להגדיר טיפוס המשתלב בשפה בדומה לטיפוסים המובנים נגדיר עבורו אופרטורים. דבר זה נקרא '''העמסת אופרטורים''' כיוון שאנו מעמיסים אופרטורים נוספים לאופרטורים הקיימים. המטרה העיקרית בהעמסת אופרטורים היא נוחות וקריאות הקוד, הרי הקוד {{קוד בשורה|<nowiki>a = b + c</nowiki>}} קריא יותר למתכנת מאשר {{קוד בשורה|<nowiki>a.set(add(b, c))</nowiki>}}. שימו לב: בשני הביטויים האלה אנו לא יודעים מהי באמת התוצאה כי לא ידועים לנו לא הטיפוסים של a, b, c ולא המשמעות של =, +, add, set עבור טיפוסים אלה.
שורה 4 ⟵ 5:
== הגדרת פונקציית האופרטור ==
 
מבחינת המהדר אופרטור הוא פונקציה שכדי לקרוא לה נשתמש בתחביר שונה מזה של קריאה לפונקציה רגילה. להלן חלק מהגדרת מבנה המייצג ווקטור מתמי דו מימדי ואופרטורופונקציית האופרטור המחבר שני ווקטורים:
<div style="direction: ltr;"><source lang="cpp">
struct Vector2D
שורה 22 ⟵ 23:
נסביר את הכתוב:
* הגדרנו טיפוס זה כמבנה ולא כמחלקה כיוון שאין לנו צורך להסתיר את משתניו, הרי לא קיימים ערכים בלתי חוקיים עבור רכיבי הווקטור ודווקא נרצה לתת אפשרות לגשת למשתנים אלו: {{קוד בשורה|<nowiki>cout << vec.x << ',' << vec.y;</nowiki>}}.
* שם פונקציית האופרטור מורכב מהמילה operator השמורה וסימן האופרטור עצמו, במקרה זה +.
* פונקציית האופרטור מקבלת מספר פרמטרים לפי מספר האופרנדים, במקרה זה שני פרמטרים. אלה משתניםמשתני מיוחסיםיחוס קבועים, הם יתיחסו אל האופרנדים של האופרטור במקום הקריאה. הגדרנו את הפרמטרים כמשתניםכמשתני מיוחסיםיחוס כי גודל האובייקט Vector2D גדול יחסית, אולם ניתן גם לתת לפרמטרים את טיפוס את אופרנד עצמו, לא בהכרח משתנה מיוחס.
* פונקציית האופרטור מחזירה אובייקט Vector2D שבו נמצאת התוצאה של חיבור שני הווקטורים. לא נוכל להחזיר משתנה מיוחסיחוס כיוון שאנו מחזירים אובייקט לוקלי.
* פונקציית האופרטור מוגדרת כ-inline כי היא קצרה ומחזירה אובייקט גדול יחסית. נגדיר את רוב האופרטורים הפשוטים כ-inline.
 
עכשיו נוכל להשתמש באופרטור שהגדרנו עבור טיפוס הווקטור, כאילו ש-Vector2D הוא טיפוס מובנה:
<div style="direction: ltr;"><source lang="cpp">
Vector2D foo = {10, 20};
Vector2D bar = {5, 0};
Vector2D klop = foo + bar;
</source></div>
 
לאחר ביצוע קטע זה במשתנה klop ימצא הערך {{קוד בשורה|<nowiki>{15.0, 20.0}</nowiki>}}.
שימו לב שאנחנו לא חייבים להגדיר את האופרטור = ואת הבנאי המעתיק כי משמעותם כברירת מחדל מתאימה לטיפוס זה.
 
כאשר הטיפוס של האופרנד הראשון הוא טיפוס אותו הגדרנו בכוחות עצמינו, נוכל להגדיר את פונקציית האופרטור כפונקציית הטיפוס שלנו (מחלקה, מבנה או אפילו איגוד). את חלק מהאופרטורים אנו חייבים להגדיר בדרך זו, למשל את {{קוד בשורה|<nowiki>operator +=</nowiki>}}:
<div style="direction: ltr;"><source lang="cpp">
struct Vector2D
{
// ...
 
Vector2D& operator += (const Vector2D &b)
{
x += b.x;
y += b.y;
return *this;
}
};
</source></div>
 
כאשר נקראת פונקציית אופרטור זו, קיים עבורה מצביע this, כלומר היא נקראת עבור מופע מסויים של מחלקה. מסיבה זו לפונקצייה זו יש רק פרמטר אחד, על אף שאופרטור עצמו הוא בינארי (בעל שני אופרנדים).
 
הערך המוחזר כאן הוא משתנה יחוס המתייחס לאובייקט אותו הגדלנו באמצעות האופרטור. טריק זה מקובל עבור רוב אופרטורים המשנים את האובייקט בכלל ואופרטורי השמה בפרט, דבר זה מאפשר לשרשר את האופרטורים, כך שהערך המוחזר מפעולה אחת יהיה לאופרנד של הפעולה הבאה, לדוגמה:
<div style="direction: ltr;"><source lang="cpp">
Vector2D foo = {1, 1};
Vector2D bar = {5, -5};
foo = (bar += foo);
</source></div>
 
תחילה יתבצע הביטוי {{קוד בשורה|<nowiki>(bar += foo)</nowiki>}}, ערך משתנה bar יהפוך ל-{{קוד בשורה|<nowiki>{6.0, -4.0}</nowiki>}}, ביטוי זה יחזיר משתנה יחוס ל-bar אשר ישמש לנו כאופרנד לפעולת ההשמה. כשתתבצע פעולת ההשמה, יועתק ערכו החדש של bar למשתנה foo.
 
== קריאה לפונקציית האופרטור ==
 
כאמור פונקציית האופרטור היא פונקציה רגילה בעלת שם מיוחד אשר ניתן לקרוא לה בצורה קצרה, ע"י שימוש בסימן האופרטור. נוכל לקרוא לפונקציית האופרטור באופן גלוי, בעמצעות שמה המלא, להלן שתי שורות מהדוגמות שלמעלה בהן קוראים לפונקציות האופרטור בדרך זו:
<div style="direction: ltr;"><source lang="cpp">
Vector2D klop = operator + (foo, bar);
foo.operator = (bar.operator += (foo));
</source></div>
 
לעיתים לא תהיה לנו ברירה אלא לציין שם מלא זה, למשל כשנרצה לקבל מצביע לפונקציית האופרטור.
 
''טיפ:'' כשתתקלו בביטוי מסובך ולא ברור נסו לדמיין או לכתוב אותו בצורה כזאת ולהבין אילו בדיוק אופרטורים נקראים לפי טיפוסי האופרנדים שלהם.
 
כל הגדרה של אופרטור נוסף היא העמסה שלו לאופרטוים הקיימים, כולל אלה המובנים בשפה. נוכל להעמיס פונקציות רבות עבור אותו האופרטור כשהן נבדלות בטיפוס האופרנדים שלהן. במקרה זה ישתמש המהדר באותם כללים כמו בהעמסת פונקציות כדי להחליט לאיזה מן האופרטורים לקרוא. דוגמות ראו בהמשך הפרק.