C++/מצביעים והמשתנה הפניה
מהו מצביע?
[עריכה]מצביע הוא משתנה המכיל כתובת זיכרון. בעזרת מצביעים ניתן לגשת לכתובות בזיכרון, לקרוא מהן, ולכתוב אליהן. אחת התועלות שישנן במצביעים היא שניתן להשתמש בהם כדי לחסוך בזיכרון ולהעביר כתובות של עצמים במקום ליצור את העצמים שוב ושוב. כמו כן, המצביעים נותנים לנו אפשרויות גמישות יותר לניהול זיכרון.
int *num = reinterpret_cast<int *>(7);
גם המרה בסגנון הישן תעבוד:
int *num = (int *)7;
בשני המקרים הנ"ל, אנו מגדירים מצביע מסוג int (כלומר, הוא מצביע לשטח בזיכרון בגודל של int). מצביע זה מצביע על הכתובת 7 בזיכרון.
אמנם בשפת C אפשר להציב ערך שלם ישירות לתוך מצביע, אך ב-++C נדרשת המרה מפורשת על מנת לאפשר הצבה כזו. יוצא מן הכלל הוא הצבת מספר 0 - עבור תאימות לאחור עם מצביע "אפס" הישן - NULL. NULL משמש לאיפוס מצביעים ומוגדר בפועל כמספר שלם 0. לפי התקן המעודכן של ++C משתמשים במילה שמורה nullptr לאיפוס מצביעים:
int *num = 0; // הגדרת מצביע למספר שלם עם הצבה מפורשת של 0
int *num = NULL; // הגדרת מצביע למספר שלם עם הצבת מצביע "אפס" בסגנון הישן
int *num = nullptr; // C++ הגדרת מצביע למספר שלם עם הצבת מצביע "אפס" לפי התקן המעודכן של
להלן דוגמה להגדרת מצביע המצביע לכתובת של משתנה אחר:
char c = 'A';
char *p = &c;
גישה למשתנה תתצבע באמצעות הוספת כוכבית (*) לפני שם המצביע. לדוגמה, בהמשך לדוגמת הקוד הקודמת נוכל לשנות את תוכנו של המצביע באמצעות:
*p = 'B';
מהו משתנה הפניה?
[עריכה]המשתנה הפניה פועל בדומה למצביע ונועד במקרים מסוימים להחליף אותו.
למשתנה הפניה ישנם כמה יתרונות וחסרונות לעומת המצביע. גם בעזרתו ניתן להשפיע בעקיפין על משתנה אחר, אלא שהוא חוסך את הכתיבה המסורבלת של אופרטורים נוספים. אפשר להבין אותו גם כשם חדש למשתנה שכבר הוכרז בזיכרון.
גם כאן, כמו במצביע, השימוש העיקרי של משתנה הפניה הוא בהעברתו כארגומנט לפונקציה, במקרה שאנו רוצים שהוא יושפע ממנה.
יישום בדוגמה פשוטה
[עריכה]הבה נגדיר משתנה הפניה:
int num;
int &ref = num;
למשתנה הפניה קראנו ref, הכרזנו אותו כמפנה לטיפוס int, והקדמנו לשמו את התו &.
בדומה למקרה של הכרזת מצביע, שם נקדים את התו *.
הפננו את המשתנה ref למשתנה num.
שימו לב: לא התרחשה כאן השמת ערכים, אלא הפניה של המשתנה ref למשתנה num, כזו הפניה אגב, יכולה להתרחש רק פעם אחת בחייו של משתנה הפניה, והיא חייבת להתבצע בהכרזה שלו.
עכשיו אם נשנה את ערכו של משתנה ref, ישתנה גם ערכו של משתנה num, וגם להפך:
ref = 10;
cout << num; //10
num = 20;
cout << ref; //20
ניתן לראות שיצירת הפניה למשתנה דומה לנתינת שם חדש, נרדף, לאותו משתנה. את אותו הדבר כמובן ניתן ליישם בעזרת מצביעים, אם כי בדרך זו היינו זקוקים לשימוש רב באופרטורים.
שימוש בפונקציות
[עריכה]כמו שאמרנו קודם, השימוש העיקרי של משתני הפניה, כמו של מצביעים, הוא בפונקציות. לדוגמה, נכתוב פונקציה בשם ()swap שמחליפה בין ערכיהם של שני משתנים:
#include<iostream.h>
void swap(int &a, int &b);
int main() {
int num1 = 5, num2 = 10;
swap(num1, num2);
cout << num1 << endl << num2;
return 0;
}
void swap(int &a,int &b) {
int temp;
temp = a;
a = b;
b = temp;
}
שימו לב שהפרמטרים המועברים לפונקציה, הם ללא שום אופרטור מצביע.
ובכל זאת הפונקציה משפיעה על המשתנים num1 ו-num2 שנמצאים בתוכנית.
בתוך הפונקציה, הגישה לפרמטרים a ו-b היא שגרתית כאילו הם משתנים רגילים, לאחר שכל אחד מהם כבר מופנה למשתנה שנשלח לו בארגומנט, בהתאמה.
הפונקציה כמובן אינה מחזירה כלום ולכן מוגדרת כ-void.
משתני הפניה כערכים מוחזרים
[עריכה]ניתן לייצור משתנה הפניה, שיוחזר מפונקציה (בשורות הקוד שלהלן num הוא משתנה גלובלי כלשהו):
int &getnum() {
int &ref = num;
return ref;
}
הטיפוס המוחזר של הפונקציה ()getnum הוא ערך של int בהפניה. אפשר להשתמש בפונקציה בכדי לקבל את ערכו של num כך:
cout << getnum();
פעולה כזאת אפשרית גם ללא משתני הפניה, ולכן דרך יותר מעניינת היא להציב ערך לnum דרך הפונקציה:
getnum() = 50;
עכשיו ערכו של num הוא 50.
- | מצביעים והמשתנה הפניה | - |