מה אופרטורים עושים
אופרטור הוא סמל שמבצע פעולה על ערך אחד או יותר, שנקראים אופרנדים. כבר השתמשתם באחד: ה-= שקושר ערך למשתנה. C++ מגיעה עם מבחר עשיר של אופרטורים לחשבון, השוואה, לוגיקה ומניפולציה של סיביות, ועם כמה קצוות חדים שמכשילים מתחילים.
בעמוד הקודם ראיתם איך const נועל ערך. אופרטורים הם הדרך לחשב את הערכים שאתם שומרים, בין אם הם קבועים ובין אם לא. בואו נעבור על המשפחות שתשתמשו בהן כל יום.
אופרטורים חשבוניים
חמשת האופרטורים החשבוניים הם +, -, *, / ו-% (מודולו, השארית של חילוק):
המלכודת הגדולה נמצאת במנה. 17 / 5 מדפיס 3, לא 3.4. כששני האופרנדים שלמים, / מבצע חילוק שלמים וזורק את החלק השברי: הוא קוטם לכיוון אפס, הוא לא מעגל. אם רוצים שבר אמיתי, לפחות אופרנד אחד חייב להיות נקודה צפה:
האופרטור % עובד רק על שלמים. שימוש ב-% על double הוא שגיאת קימפול: השתמשו ב-std::fmod מ-<cmath> לשאריות של נקודה צפה.
השמה ואופרטורים מורכבים
= בודד משים, הוא לא משווה. C++ נותנת צורות מורכבות שמשלבות פעולה עם השמה, כדי שלא תצטרכו לחזור על שם המשתנה:
טעות קלאסית וכואבת היא לכתוב = כשהתכוונתם ל-== בתוך תנאי. if (x = 0) משים 0 ל-x ואז בודק את התוצאה (שנחשבת שקר), במקום להשוות. קומפיילרים מודרניים מזהירים על זה כשמפעילים אזהרות: השאירו את -Wall פעיל והתייחסו לאזהרה ברצינות.
אופרטורי השוואה
אופרטורי השוואה שואלים שאלת כן או לא ומפיקים bool (true או false):
כברירת מחדל cout מדפיס bool כ-1 או 0. שלחו boolalpha ל-stream פעם אחת, והוא עובר למילים true/false לכל המשך ה-stream הזה.
מלכודת עדינה: אל תשרשרו השוואות כמו 1 < x < 10. זה מתפרש כ-(1 < x) < 10: ההשוואה הראשונה מפיקה bool (0 או 1), שמושווה אחר כך ל-10, כך שכל הביטוי כמעט תמיד true. כתבו במקום זאת 1 < x && x < 10.
אופרטורים לוגיים והערכה מקוצרת
&& (וגם), || (או) ו-! (לא) משלבים ביטויים בוליאניים. שני הראשונים מבצעים הערכה מקוצרת (short-circuit): ההערכה נעצרת ברגע שהתוצאה ידועה.
לא check("A") ולא check("B") רצים אף פעם: זו הערכה מקוצרת. זו לא רק אופטימיזציה, זה כלי. אפשר לכתוב בבטחה if (ptr != nullptr && ptr->ready), כי מגיעים לחלק ptr->ready רק כש-ptr אינו null, וכך נמנעים מגישה דרך מצביע תלוי.
הגדלה והקטנה
++ מוסיף אחד, ו--- מחסר אחד. לכל אחד יש צורה מקדימה (prefix) וצורה מאוחרת (postfix), וההבדל חשוב כשמשתמשים בתוצאה:
כשרוצים רק את תופעת הלוואי (למשל בלולאת for), העדיפו ++i. ב-int רגיל זה זהה, אבל בטיפוסים כבדים יותר כמו איטרטורים, הגדלה מאוחרת צריכה להעתיק קודם את הערך הישן, וזו עבודה מבוזבזת.
אזהרה נוספת: אל תשנו את אותו משתנה פעמיים בביטוי אחד, כמו i = i++ + 1; או arr[i] = i++;. הסדר של העדכונים האלה לא מוגדר, והתוצאה היא התנהגות לא מוגדרת. הגבילו כל משתנה לשינוי אחד בכל פקודה.
אופרטורים על סיביות וקדימות
לעבודה ברמה נמוכה יש אופרטורים על סיביות: & (וגם), | (או), ^ (xor), ~ (לא), וההזזות << ו->>.
שימו לב: << ו->> הם גם אופרטורי ה-stream של cout. בתוך שורת cout בדרך כלל צריך סוגריים סביב הזזת סיביות, אחרת הקומפיילר קורא אותה כהכנסה ל-stream.
לבסוף, קדימות קובעת מה נקשר קודם כשמערבבים אופרטורים. * ו-/ נקשרים חזק יותר מ-+ ו--, בדיוק כמו במתמטיקה, ולכן 2 + 3 * 4 הוא 14. השוואה נקשרת חלש יותר מחשבון, ו-&&/|| הלוגיים חלשים עוד יותר. כשיש ספק, אל תשננו את הטבלה המלאה: הוסיפו סוגריים. (a + b) * c ברור יותר מאשר לסמוך על כך שהקורא יזכור את הכללים.
הבא בתור: המרת טיפוסים
ראיתם למעלה ש-(double)a / b כפה על מספר שלם חילוק של נקודה צפה. זו המרה (cast): המרה מכוונת של ערך מטיפוס אחד לאחר. בעמוד הבא נעבור על כלי ההמרה של C++, מהעלאות מרומזות ועד static_cast, ומתי כל אחד מהם בטוח.
שאלות נפוצות
אילו אופרטורים יש ב-C++?
C++ מחלקת את האופרטורים לחשבון (+ - * / %), השוואה (== != < > <= >=), לוגיקה (&& || !), השמה (= += -= ...), הגדלה והקטנה (++ --) ופעולות על סיביות (& | ^ ~ << >>). יש גם את הטרנרי ?: וכמה נוספים כמו sizeof.
למה 5 / 2 שווה 2 ב-C++?
כי שני האופרנדים הם int, ולכן / מבצע חילוק שלמים והחלק השברי נזרק, לא מעוגל. כדי לקבל 2.5, הפכו לפחות אופרנד אחד לערך נקודה צפה: 5.0 / 2 או 5 / 2.0.
מה ההבדל בין ++i ל-i++ ב-C++?
שניהם מוסיפים 1 ל-i. ++i (הגדלה מקדימה) מגדיל קודם ומחזיר את הערך החדש, ו-i++ (הגדלה מאוחרת) מחזיר את הערך הישן ואז מגדיל. כשאכפת לכם רק מתופעת הלוואי, העדיפו ++i.