למה יש פונקציות
פונקציה היא בלוק קוד עם שם שאפשר להריץ לפי דרישה על ידי קריאה לו. במקום לחזור על אותה לוגיקה בכמה מקומות, כותבים אותה פעם אחת, נותנים לה שם וקוראים לשם הזה בכל מקום שצריך. כך תוכניות נשארות קצרות יותר, קלות יותר לקריאה וקלות יותר לתיקון: משנים את הלוגיקה במקום אחד וכל מי שקורא לה מקבל את העדכון.
כל הזמן הזה כבר קראתם לפונקציה אחת: main. היא נקודת הכניסה שממנה כל תוכנית C++ מתחילה. עכשיו תכתבו פונקציות משלכם. הלולאות שראיתם קודם, כמו for מבוסס טווח, נמצאות לעיתים קרובות בתוך פונקציות, כדי שאפשר יהיה להשתמש מחדש בחלק שלם של לוגיקה לפי שם.
המבנה של פונקציה
לכל פונקציה יש ארבעה חלקים: טיפוס החזרה, שם, רשימת פרמטרים בסוגריים וגוף בסוגריים מסולסלים.
int add(int a, int b) { // טיפוס החזרה | שם | פרמטרים
return a + b; // גוף
}
intהוא טיפוס ההחזרה: סוג הערך שהפונקציה מחזירה.addהוא השם שבו קוראים לה.(int a, int b)הם הפרמטרים: הקלטים שהקורא מספק.- הסוגריים המסולסלים מחזיקים את הגוף, הקוד שרץ כשקוראים לה.
הנה היא בתוכנית מלאה. הגדרת הפונקציה מעל main אומרת ש-main רואה אותה כשהיא קוראת לה.
הקריאה add(2, 3) מריצה את הפונקציה עם a = 2 ו-b = 3, והביטוי כולו הופך לערך המוחזר. אפשר לשמור אותו במשתנה או להשתמש בו ישירות בתוך ביטוי אחר, כמו ששורת ה-cout השנייה עושה.
החזרת ערך
הפקודה return עושה שני דברים: היא מחזירה ערך לקורא, והיא מסיימת את הפונקציה מיד. קוד שאחרי return שרץ לא מתבצע: השליטה קופצת ישר חזרה למקום שבו הפונקציה נקראה.
טיפוס הערך המוחזר חייב להתאים לטיפוס ההחזרה המוצהר (או להיות ניתן להמרה אליו). פונקציה שמוצהרת כ-int צריכה להחזיר int; החזרה של כלום, או הגעה לסוף בלי return, היא באג בכל פונקציה שאינה void.
פונקציות void
לא כל פונקציה מייצרת ערך. כשפונקציה רק עושה משהו, מדפיסה פלט או מעדכנת מצב, טיפוס ההחזרה שלה הוא void. פונקציית void יכולה להשתמש ב-return; לבד כדי לצאת מוקדם, או פשוט לרוץ עד הסוגר המסולסל הסוגר.
ניסיון להשתמש בתוצאה של פונקציית void, int x = greet("Ada");, הוא שגיאת הידור, כי אין ערך להשים. טעות נפוצה היא לכתוב return someValue; בתוך פונקציית void; המהדר דוחה גם את זה.
הצהרות מול הגדרות
C++ קוראת קובץ מלמעלה למטה, ולכן כברירת מחדל פונקציה חייבת להופיע לפני הקוד שקורא לה. כשהסדר הזה לא נוח, מפצלים את הפונקציה להצהרה (שנקראת גם prototype) ולהגדרה.
הצהרה מציינת את החתימה של הפונקציה ומסתיימת בנקודה-פסיק, בלי גוף. היא מבטיחה למהדר "הפונקציה הזו קיימת, וכך קוראים לה". ההגדרה המלאה יכולה להגיע אחר כך, אפילו אחרי main.
בלי ה-prototype בשורה 4, המהדר היה נתקל ב-square(5) בתוך main לפני שראה את square אי פעם, והבנייה הייתה נכשלת. prototypes הם גם הדרך שבה קובצי header מאפשרים לקובצי מקור רבים לחלוק את אותן פונקציות. שימו לב ששמות הפרמטרים בהצהרה הם אופציונליים: int square(int); עובד בדיוק אותו דבר; רק הטיפוסים חשובים למהדר.
טעויות נפוצות
כמה מלכודות תופסות מתחילים שוב ושוב:
- קריאה לפני הצהרה. אם אתם מקבלים שגיאת "
addwas not declared in this scope", הפונקציה מוגדרת מתחת לקריאה הראשונה אליה ואין לה prototype. הזיזו את ההגדרה למעלה או הוסיפו prototype. - שכחה להחזיר ערך. הגעה לסוף של פונקציה שאינה
voidבליreturnהיא undefined behavior: הקורא מקבל זבל. הדרו עם אזהרות מופעלות (-Wall) והמהדר יסמן את זה. - הגדרה מול קריאה. להגדרה יש גוף בסוגריים מסולסלים ואין נקודה-פסיק בסוף. להצהרה יש נקודה-פסיק ואין גוף. ערבוב ביניהן, כמו לשים נקודה-פסיק מיד אחרי רשימת הפרמטרים של פונקציה שהתכוונתם להגדיר, מייצר שגיאות מבלבלות.
- התעלמות מהערך המוחזר.
add(2, 3);בשורה משלו מתהדר, אבל הסכום שחושב נזרק בשקט. ודאו שאתם באמת משתמשים במה שפונקציה מחזירה.
// נראה כמו הגדרה, אבל ה-; התועה הופך את זה
// להצהרה ואחריה בלוק יתום, שגיאת הקלדה נפוצה:
int triple(int n); // <- ה-; הזה מסיים את הפקודה
{
return n * 3; // n לא מוגדר כאן; הבלוק הזה נשאר יתום
}
הבא בתור: פרמטרים של פונקציות
ראיתם פונקציות שמקבלות קלט דרך רשימת הפרמטרים שלהן, אבל יש בזה הרבה יותר. העמוד הבא צולל לפרמטרים של פונקציות: העברה לפי ערך מול לפי הפניה, ארגומנטים ברירת מחדל, פרמטרים const, ואיך הבחירה משפיעה על השאלה אם הפונקציה שלכם יכולה לשנות את הנתונים של הקורא.
שאלות נפוצות
איך כותבים פונקציה ב-C++?
נותנים לה טיפוס החזרה, שם, סוגריים לפרמטרים וגוף בסוגריים מסולסלים: int add(int a, int b) { return a + b; }. קוראים לה בשמה עם ארגומנטים, כמו add(2, 3). אם הפונקציה לא מחזירה כלום, השתמשו ב-void כטיפוס ההחזרה.
מה ההבדל בין הצהרה להגדרה של פונקציה ב-C++?
הצהרה (או prototype) אומרת למהדר את השם, טיפוס ההחזרה והפרמטרים של הפונקציה, ומסתיימת בנקודה-פסיק: int add(int a, int b);. הגדרה מספקת גם את הגוף בסוגריים מסולסלים. אפשר להצהיר על פונקציה לפני main ולהגדיר אותה אחר כך: ההצהרה מאפשרת לקרוא לה לפני שההגדרה מופיעה.
מה קורה אם פונקציה ב-C++ לא מחזירה ערך?
בפונקציית void, כלום: היא פשוט מסתיימת. אבל בפונקציה שאינה void, הגעה לסוף בלי return היא undefined behavior: הקורא מקבל ערך זבל והתוכנית עלולה להתנהג לא נכון. רוב המהדרים מזהירים על זה; תמיד החזירו ערך בכל מסלול של פונקציה שאינה void.