Menu

const וקבועים ב-C++: const, constexpr ו-consteval

איך מצהירים על ערכים לקריאה בלבד ב-C++ עם const, ההבדל בין const ל-constexpr, מצביעי const מול מצביעים ל-const, ופונקציות חברות const.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

למה יש קבועים

קבוע הוא ערך שאתם מבטיחים לא לשנות אחרי שקבעתם אותו. סימון של משהו כ-const עושה שתי עבודות בבת אחת: הוא מתעד את הכוונה שלכם לכל מי שקורא את הקוד, והוא מאפשר למהדר לאכוף את הכוונה הזו. כל שורה שמנסה לשנות את הערך הופכת לשגיאת הידור במקום לבאג שקט בזמן ריצה.

בעוד שמילת המפתח auto נתנה למהדר להסיק את הטיפוס של משתנה, const מגביל את מה שאפשר לעשות עם המשתנה הזה. השתיים משתלבות בחופשיות: const auto limit = 100; הוא int לקריאה בלבד.

הצהרה על ערך const

שימו const לפני הטיפוס. משתנה const חייב להיות מאותחל באותה שורה, כי אין רגע מאוחר יותר שבו מותר לבצע אליו השמה.

בטלו את ההערה של ההשמה והתוכנית לא תתהדר: המהדר מדווח "assignment of read-only variable". זו בדיוק הנקודה: הטעות נתפסת עוד לפני שהתוכנית רצה.

הרגל נפוץ של מתחילים שמגיע מ-C הוא #define MAX_USERS 100. הימנעו ממנו. מאקרו הוא החלפת טקסט עיוורת, בלי טיפוס ובלי כבוד לטווחי הכרזה, ולכן אי אפשר לבדוק אותו בדיבאגר והוא מייצר הודעות שגיאה מבלבלות. משתנה const (או constexpr) עובר בדיקת טיפוסים ויש לו טווח כמו לכל משתנה אחר.

const מול constexpr

שתי מילות המפתח נותנות ערך שלא יכול להשתנות, אבל הן עונות על שאלות שונות. const אומר "זה לעולם לא משתנה אחרי שנקבע". constexpr אומר את הדבר החזק יותר, "את זה אפשר לחשב בזמן הידור", וכל מה שהוא constexpr הוא אוטומטית גם const.

כלל האצבע: השתמשו ב-constexpr בכל פעם שהערך הוא ליטרל קבוע או חישוב שהמהדר יכול לבצע (גודל מערך, אורך באפר, תוויות switch, ארגומנטים של template). השתמשו ב-const רגיל כשהערך נקבע בזמן ריצה אבל לא אמור להשתנות אחר כך, כמו עותק const של ארגומנט של פונקציה.

מאז C++20 יש גם consteval, שמשמש לפונקציות שחייבות לרוץ בזמן הידור:

consteval int square(int x) { return x * x; }
constexpr int area = square(8); // מחושב בזמן ההידור

פונקציית constexpr יכולה לרוץ בזמן הידור; פונקציית consteval תמיד חייבת, אחרת זו שגיאה.

מצביעים ו-const: קוראים מימין לשמאל

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

קראו את int* const p2 מימין לשמאל: "p2 is a const pointer to int". קראו את const int* p1 כ-"p1 is a pointer to const int". אם תטעו כאן, תבזבזו זמן אמיתי בבלבול מול שגיאה שאומרת שאי אפשר לשנות משהו שחשבתם שהוא ניתן לשינוי.

מלכודת מעשית: לעולם אל תיקחו את הכתובת של const ותסירו את ה-const בהמרה כדי לשנות את האובייקט שמתחת. זה undefined behavior אם האובייקט המקורי היה באמת const, והמהדר רשאי להניח שהערך לעולם לא משתנה: ה"כתיבה" שלכם עשויה פשוט להתעלם.

הפניות const כפרמטרים של פונקציה

השימוש היומיומי הנפוץ ביותר ב-const הוא העברת אובייקטים גדולים לפי הפניה בלי להעתיק אותם. פרמטר const& חוסך את ההעתקה וגם מבטיח שהפונקציה לא תשנה את הארגומנט של הקורא.

העברה לפי const& היא הבחירה ברירת המחדל לכל פרמטר שגדול מכמה בתים (מחרוזות, vectors, המחלקות שלכם). שימו לב שהיא גם מאפשרת לפונקציה לקבל ערך זמני כמו "Grace": הפניה רגילה שאינה const לא יכולה להיקשר לערך זמני, ולכן הסרת ה-const כאן הייתה דוחה את הקריאה השנייה.

פונקציות חברות const

כשאתם כותבים מחלקה, סמנו כל מתודה שלא משנה את האובייקט עם const בסוף. זה מה שמאפשר לקרוא למתודה על מופעים const ועל פרמטרים const&; בלי זה, לא תוכלו לקרוא את האובייקט שלכם דרך ידית const.

המשמעת של סימון מתודות לקריאה בלבד כ-const נקראת const correctness. עשו את זה נכון מוקדם: קל להוסיף const למתודה שהייתה צריכה להיות const, אבל להוסיף const בדיעבד לאורך בסיס קוד גדול זה כואב, כי כל קורא שעובר דרך הפניית const תלוי בזה.

הבא בתור: אופרטורים

עכשיו כשאפשר לנעול את הערכים שלכם עם const, השלב הבא הוא לעשות איתם דברים. עמוד האופרטורים עוסק באופרטורים אריתמטיים, אופרטורי השוואה, אופרטורים לוגיים ואופרטורי השמה, כולל המלכודות של חילוק שלמים, קדימות אופרטורים, והאופן שבו const מתנהג מול אופרטורי ההשמה שאסור לכם להשתמש בהם.

שאלות נפוצות

מה ההבדל בין const ל-constexpr ב-C++?

const אומר שהערך לא יכול להשתנות אחרי האתחול, אבל אפשר שהוא יחושב בזמן ריצה. constexpr חזק יותר: הוא מבטיח שאפשר לחשב את הערך בזמן הידור, כך שאפשר להשתמש בו במקומות שדורשים קבוע של זמן הידור (גודל מערך, ארגומנטים של template, תוויות switch). כל אובייקט constexpr הוא גם const, אבל לא כל אובייקט const הוא constexpr.

איך מצהירים על קבוע ב-C++?

שמים const לפני הטיפוס ונותנים ערך: const int maxUsers = 100;. משתנה const חייב להיות מאותחל בזמן ההצהרה, כי לעולם לא תוכלו לבצע אליו השמה אחר כך. לקבועים של זמן הידור העדיפו constexpr int maxUsers = 100;. הימנעו מהמאקרו הישן בסגנון C, #define: אין לו טיפוס והוא מתעלם מטווחי הכרזה.

מה המשמעות של מצביע const ב-C++?

זה תלוי איפה ה-const יושב. const int* p הוא מצביע ל-const: אפשר לכוון את p למקום אחר, אבל אי אפשר לשנות את *p. int* const p הוא מצביע const: אפשר לשנות את *p, אבל אי אפשר לכוון את p למקום אחר. קראו את ההצהרה מימין לשמאל: int* const הוא "const pointer to int".

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל