מה זו מחלקה
מחלקה היא תוכנית אדריכלית לטיפוס מותאם אישית שמאגד נתונים (משתנים חברים) והתנהגות (פונקציות חברות) ליחידה אחת. בעוד שטיפוסים מובנים כמו int ו-double מתארים ערכים בודדים, מחלקה מאפשרת לכם למדל מושג שלם, חשבון בנק, נקודה במישור, שחקן, כערך יחיד שאפשר להעביר ממקום למקום.
כבר השתמשתם במחלקות בלי להגדיר אף אחת: std::string והמכל vector הם מחלקות מהספרייה הסטנדרטית. קריאה ל-name.length() או ל-v.push_back(3) היא קריאה לפונקציה חברה על אובייקט. עכשיו תבנו טיפוסים משלכם באותה דרך, ואחר כך תעברו לבנאים שמאתחלים אותם.
הגדרת מחלקה ויצירת אובייקטים
הגדרת מחלקה מפרטת את החברים שלה בין סוגריים מסולסלים ומסתיימת בנקודה-פסיק; שכחה של ה-; הזה בסוף היא אחת הטעויות הנפוצות ביותר של מתחילים. כל אובייקט שיוצרים מהמחלקה מקבל עותק משלו של המשתנים החברים.
rex ו-luna הם שני אובייקטים נפרדים. שינוי של rex.name לא נוגע ב-luna.name: כל אובייקט מחזיק את הנתונים שלו. ניגשים לחבר עם אופרטור הנקודה . על אובייקט (או עם -> כשיש מצביע אליו).
public מול private
כברירת מחדל, כל מה שבתוך class הוא private: רק הפונקציות החברות של המחלקה עצמה יכולות לגעת בו. התווית public: פותחת חברים לקוד חיצוני. החלוקה הזו היא לב הכימוס: חושפים ממשק בטוח ומסתירים את הנתונים הפנימיים כך שאי אפשר להכניס אותם למצב לא תקין.
מכיוון ש-count הוא private, אף קוד קורא לא יכול להבריח פנימה ערך שלילי או חסר היגיון: הוא חייב לעבור דרך increment(). ה-const שאחרי רשימת הפרמטרים של value() מבטיח שהפונקציה לא תשנה את האובייקט, מה שמאפשר לקרוא לה על אובייקטים const ומבהיר את הכוונה לקוראים.
הצהרה מול הגדרה של מתודות
במחלקות קטנות, הגדרת מתודות בתוך המחלקה (כמו למעלה) היא בסדר. בגדולות יותר מקובל להצהיר על המתודות בתוך המחלקה ולהגדיר את הגוף שלהן מחוץ לה, עם תחביר הטווח ClassName::method. כך הגדרת המחלקה נשארת קריאה, כסיכום של הממשק.
הקידומת Rectangle:: אומרת למהדר לאיזו מחלקה הפונקציה שייכת. בתוך הגדרה כזו עדיין פונים לחברים בשמם בלבד (width, area()): המהדר יודע שהם שייכים לאובייקט שעליו נקראה המתודה.
המצביע this
בתוך כל פונקציה חברה שאינה סטטית, this הוא מצביע לאובייקט שעליו הופעלה הפונקציה. בדרך כלל לא צריך אותו, כי שם חבר לבד כבר מתייחס לאובייקט הנוכחי. הוא מוכיח את עצמו כשפרמטר מסתיר חבר, כלומר יש להם אותו שם, וצריך להבחין ביניהם.
בלי this->, כתיבת x = x; בתוך setX הייתה משימה את הפרמטר לעצמו ומשאירה את החבר ללא שינוי: באג שקט. this->x מסיר את העמימות. בסיסי קוד רבים נמנעים מהבעיה לגמרי בכך שהם נותנים לפרמטרים שמות אחרים (למשל setX(int newX)), אבל תראו את this-> כל הזמן בקוד אמיתי.
טעויות נפוצות
כמה מלכודות של מחלקות תופסות אנשים שוב ושוב:
- שכחת הנקודה-פסיק הסוגרת. הגדרת מחלקה מסתיימת ב-
};. השמיטו את ה-;ותקבלו מבול של שגיאות מבלבלות שמצביעות על מה שבא אחרי המחלקה, לא על המחלקה עצמה. - שכחה לאתחל חברים. חברים מטיפוסים מובנים כמו
intו-doubleלא מאופסים אוטומטית. קריאה של חבר לא מאותחל היא undefined behavior. תנו לחברים ערכי ברירת מחדל (int count = 0;) או אתחלו אותם בבנאי. - גישה לחברים פרטיים מבחוץ.
c.count = 5;על חבר פרטי היא שגיאת הידור: עברו דרך מתודה ציבורית. זה הכימוס שעובד כמתוכנן. - בלבול בין המחלקה לאובייקט.
Dog.bark();שגוי:Dogהוא הטיפוס. קוראים למתודות על אובייקטים:rex.bark();.
// חבר לא מאותחל: קריאה של 'age' היא undefined behavior:
class Cat {
public:
int age; // אין ערך ברירת מחדל
};
Cat c;
std::cout << c.age; // ערך זבל, לא 0
הבא בתור: בנאים
השמה ידנית לכל חבר אחרי יצירת אובייקט, rex.name = ...; rex.age = ...;, מייגעת וקל לשכוח אותה, וזו בדיוק הדרך שבה מגיעים לחברים לא מאותחלים. העמוד הבא עוסק בבנאים (constructors): פונקציות מיוחדות שרצות אוטומטית כשאובייקט נוצר, ומאפשרות להבטיח שכל אובייקט מתחיל במצב תקין עם תחביר נקי של שורה אחת, Dog rex("Rex", 4);.
שאלות נפוצות
מה ההבדל בין מחלקה לאובייקט ב-C++?
מחלקה היא התוכנית האדריכלית: היא מתארת אילו נתונים (משתנים חברים) ואיזו התנהגות (פונקציות חברות) יש לטיפוס. אובייקט הוא מופע קונקרטי שנבנה לפי התוכנית הזו. class Dog { ... }; מגדיר את הטיפוס פעם אחת; Dog rex; יוצר Dog אמיתי שאפשר להשתמש בו. מחלקה אחת יכולה לייצר אובייקטים עצמאיים רבים.
מה ההבדל בין class ל-struct ב-C++?
מבחינה טכנית רק רמת הגישה שבברירת המחדל: החברים של class הם private כברירת מחדל, ואילו החברים של struct הם public כברירת מחדל. לשניהם יכולים להיות פונקציות חברות, בנאים וירושה. לפי המוסכמה, struct משמש לאוספי נתונים פשוטים ו-class לטיפוסים עם התנהגות ואינווריאנטים שצריך להגן עליהם.
מה עושה המצביע this במחלקה ב-C++?
בתוך פונקציה חברה, this הוא מצביע לאובייקט שעליו נקראה הפונקציה. משתמשים בו כדי להבחין בין פרמטר לבין חבר (this->x = x;) או כדי להחזיר את האובייקט הנוכחי. לגישה רגילה לחברים כמעט אף פעם לא צריך אותו: כתיבת x כבר מתייחסת ל-x של האובייקט הנוכחי.