Menu

Access Specifiers ב-C++: public, private, protected

איך public, private ו-protected קובעים מי יכול לגשת לחברים של מחלקה ב-C++: הבסיס של כימוס (encapsulation), עם getters, setters ופתח המילוט friend.

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

מי רשאי לגעת בנתונים שלכם

כשכתבתם את המחלקה הראשונה שלכם, כנראה חשפתם כל חבר שלה לעולם החיצון. זה עובד, אבל זה מוותר על אחת הסיבות העיקריות לקיומן של מחלקות: כימוס (encapsulation), כלומר הסתרת המצב הפנימי של מחלקה כך ששאר התוכנית תוכל לתקשר איתה רק דרך ממשק מבוקר. Access specifiers הם הדרך לסמן את הגבול הזה.

יש בדיוק שלושה: public, private ו-protected. כל אחד מהם מתייג את החברים שבאים אחריו, והתווית קובעת איזה קוד רשאי לקרוא או לכתוב אותם. אם עושים את זה נכון, המחלקה אוכפת את הכללים של עצמה; אם טועים, כל באג בכל מקום יכול להשחית את המצב של האובייקט.

שלושת ה-specifiers

Specifier הוא מילת מפתח ואחריה נקודתיים. כל חבר שמוצהר אחריו, עד ה-specifier הבא, נמצא תחת רמת הגישה הזו.

בטלו את ההערה בשורה האחרונה והמהדר יסרב לבנות: balance הוא private, ולכן main לא יכולה לגעת בו ישירות. זו בדיוק הנקודה: הדרך היחידה לשנות את היתרה היא דרך deposit, כלומר בהמשך תוכלו להוסיף בדיקות תקינות (בלי הפקדות שליליות, רישום ללוג, מגבלות) במקום אחד ולסמוך על כך שהן תמיד מופעלות.

הנה הפירוט המלא:

//             נגיש מ...
// public      כל מקום (כל קוד שיש לו את האובייקט)
// private     רק החברים של אותה מחלקה (+ friends)
// protected   החברים של אותה מחלקה וגם מחלקות נגזרות (+ friends)

class מול struct: ברירת המחדל

אפשר לכתוב כמה בלוקים של specifiers שרוצים, בכל סדר. מה שהחברים מקבלים לפני שכותבים את הראשון תלוי בשאלה אם השתמשתם ב-class או ב-struct:

  • ב-class, החברים הם private כברירת מחדל.
  • ב-struct, החברים הם public כברירת מחדל.

ברירת המחדל הזו היא ההבדל היחיד ברמת השפה בין שתי מילות המפתח. ל-struct יכולים להיות מתודות, בנאים ואזורי private בדיוק כמו ל-class.

המוסכמה היא להשתמש ב-struct לאוספים פשוטים של נתונים ציבוריים וב-class כשרוצים התנהגות ומצב מוסתר, אבל המהדר לא אוכף את זה; רק ברירת המחדל שונה.

כימוס עם getters ו-setters

הדפוס היומיומי הוא: הנתונים נכנסים לאזור private, ומתודה public נותנת גישה מבוקרת. getter לקריאה בלבד מחזיר את הערך; setter בודק תקינות לפני ההשמה. כאן private מוכיח את עצמו.

מכיוון ש-celsius הוא private, אין דרך להבריח פנימה ערך לא תקין: כל כתיבה חייבת לעבור דרך setCelsius, ששומרת על האינווריאנט. שימו לב שה-getters מסומנים const: הם מבטיחים לא לשנות את האובייקט, כך שאפשר לקרוא להם גם על אובייקטים מסוג const Temperature.

protected וירושה

protected רלוונטי רק כשירושה נכנסת לתמונה. עבור קוד חיצוני הוא מתנהג כמו private, אבל מחלקה נגזרת יכולה לגשת אליו. השתמשו בו לחברים שתת-מחלקה באמת צריכה, אבל הציבור עדיין לא אמור לראות.

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

פתח המילוט friend

לפעמים פונקציה או מחלקה חיצונית אחת באמת צריכה לראות את הפנים שלכם. מקרה קלאסי הוא העמסת האופרטור <<, שאי אפשר להפוך אותו לחבר במחלקה. מילת המפתח friend נותנת לישות האחת הזו, בשמה, גישה לחברים ה-private וה-protected שלכם, ולא יותר.

friend הוא חריג מכוון וכירורגי: המחלקה עצמה מציינת בדיוק במי היא בוטחת, כך שאף אחד לא יכול להעניק לעצמו גישה מבחוץ. השתמשו בו במשורה; אם אתם מוצאים את עצמכם מוסיפים הרבה friends, כנראה שהחברים לא היו צריכים להיות private מלכתחילה, או שהעיצוב שלכם צריך מחשבה מחודשת.

טעויות נפוצות שכדאי להימנע מהן

  • להפוך כל חבר ל-public. זה מרגיש קל, אבל מאבדים את כל בדיקות התקינות והאינווריאנטים שכימוס נותן. ברירת המחדל שלכם צריכה להיות נתונים private עם מתודות public.
  • לשכוח את ברירת המחדל של class. class Foo { int x; }; הופך את x ל-private, ולכן foo.x = 5 לא יתהדר. אם התכוונתם לאוסף נתונים פשוט, השתמשו ב-struct או הוסיפו תווית public:.
  • שימוש מופרז ב-protected. זה גבול חלש יותר מ-private ורלוונטי רק עם ירושה. שימוש בו בכל מקום קושר תת-מחלקות לשדות שאולי תרצו לשנות.
  • לצפות ש-private יהיה מנגנון אבטחה. זה כלל בזמן הידור שמונע גישה בטעות, לא הצפנה. הבתים עדיין נמצאים בזיכרון; private נוגע לעיצוב נקי, לא לסודיות.

הבא בתור: Structs

ראיתם עכשיו ש-struct הוא בעצם class שהחברים שלו public כברירת מחדל. העמוד הבא, structs, מעמיק במקרים שבהם ברירת המחדל הציבורית הזו היא בדיוק מה שרוצים, אגרגטים קלים לקיבוץ ערכים קשורים, ובאופן שבו משתמשים ב-struct ב-C++ אידיומטי לצד מחלקות מלאות.

שאלות נפוצות

מה ההבדל בין public, private ו-protected ב-C++?

לחברים public אפשר לגשת מכל מקום. לחברים private אפשר לגשת רק מתוך אותה מחלקה (ומה-friends שלה). protected דומה ל-private, אבל מאפשר גם למחלקות נגזרות לגשת לחבר. לפי המוסכמה, שומרים את הנתונים private וחושפים התנהגות דרך מתודות public.

האם חברים הם private כברירת מחדל ב-C++?

ב-class, כן: הכול private עד שכותבים access specifier. ב-struct, ברירת המחדל היא public. ברירת המחדל הזו היא ההבדל האמיתי היחיד בין class ל-struct ב-C++; לשניהם יכולים להיות מתודות, בנאים ו-access specifiers.

מה עושה מילת המפתח friend ב-C++?

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

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

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

להתחיל