ההבדל בין מחלקה לאובייקט

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

בפסקל מחלקה (class) היא מצביע לאובייקט. כלומר בפסקל האובייקט הוא בסיס, והמחלקה נמצאת מעליו. אנחנו מגדירים את המשתנה בתור "מופע של אובייקט" או instance of object.

השימוש באובייקט בשפת פסקל ד"א, נוצר על ידי חברת אפל (אותה יצרנית iphone, mac וכו') ב1982, ובסוף שנות ה80, גם חברת בורלנד אימצה לעצמה את התחביר, ובעצם הגרסה 5.5 של טורבו פסקל (ואילך) תומכת בתחביר של אובייקט.

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

באובייקט בניגוד למחלקה אין ניהול זכרון פנימי, ואין בסיס שאנחנו יורשים בכל מקרה. הסיבה לירושה במחלקות בפסקל, היא אתחול הזכרון, אשר מספק גם RTTI, דבר שלא קיים באובייקטים בפסקל, ולכן גם אין את חלק ה published אשר קיים במחלקות, ובעצם גלוי ל RTTI. כך שלמעשה אובייקט בפסקל מזכיר מאוד את המחלקה ב++C מבחינת ניהול זיכרון, אבל עדיין שונה מאוד ממה שאנחנו מכירים מ ++C.

ככה נראה אובייקט בפסקל:

type
PMyObject = ^TMyObject;
TMyObject = object
public
constructor Init;
destructor Done;
end;

בניגוד למחלקה, שם אנחנו נשתמש ב יוצר בשם Create והורס בשם Destroy, כאן אנחנו מחוייבים להשתמש בשמות Init ליוצר ו Done להורס.

האתחול וההריסה של האובייקט עובדים בצורה הבאה:

...
var
...
MyObject : PMyObject;
...
New(MyObject, init);
...
Despose(MyObject, Done);
...

כמו שניתן לראות, אנחנו מנהלים את הזכרון שלנו עם מצביעים. new היא ממש כמו new ב++C, התפקיד של הפרוצדורה הוא לאתחל את הזכרון בצורה דינאמית בהתאם לצורך, בלי לציין גודל מסויים (בניגוד ל getmem – מקבילה ל malloc בפסקל). כמובן שבשביל לשחרר אנחנו משתמשים בפרוצדורה Despose אשר משחררת את הזכרון ש new הקצתה.

בעבר (שזה אומר TP, וגרסאות שונות של דלפי), אתחול של אובייקט השתמש ב new בתור פונקציה, ונכתב בצורה הבאה:

MyObject := new(PMyObject, init);

השיטה ד"א גם כיום נתמכת, אך עדיף לא להשתמש בה (שימו לב שזה השימוש היחיד שמורשה בפסקל ל new בתור פונקציה).

אבל כיום כאמור, השיטה הזו כבר לא בשימוש (לפחות ב FPC).

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

11 מחשבות על “ההבדל בין מחלקה לאובייקט

  1. Shai

    עידו,

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

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

    למעשה, אני לא בטוח שהייתי מבין על מה אתה מדבר, אלולא היכרתי את הנושא מ-#C (שם מבדילים בין class לבין struct, שמות ממסורת ++C — ועם כל הכבוד, struct כאן הוא שם הרבה יותר מוצלח מ-object). מקובל שם להגדיר את ההבדל הבסיסי כך: מופעים של class מוקצים על הערימה, ואלו של struct – על המחסנית. אם אתה יודע קצת C, כמעט כל יתר ההבדלים נגזרים מזה.

    כמובן, המקור הוא בפסקל — האיש המוביל בפיתוח של #C הוא אנדרס היילסברג, שהוביל בזמנו את TP. אבל הניסוח של מיקרוסופט יותר מוצלח. זה, דרך אגב, נכון גם לגבי Java, שהיא המקור של רוב #C: מיקרוסופט לא סתם העתיקו, הם שיפרו.

  2. ik_5 מאת

    פרט להגדרה שמחלקה היא מצביע של אובייקט (תרגום ישיר מאנגלית ד"א), היה לי מאוד קשה כמובן לנסח את זה. אבל לגבי struct המקביל שלו בפסקל הוא דווקא record (לפחות אם אתה משווה את C).

    את ההגדרה ניתן למצוא ד"א כאן

  3. Shai

    אני מסכים — הניסוח המבולבל במקור (אם כי המקור גם מתייחס במפורש להבדל בין ה-stack וה-heap).

    לגבי struct — דיברתי ספציפית על #C, לא על C.

    אבל אם כך — מה ההבדל בין object ל-record? הצצתי קצת בתיעוד, ומשם נראה שההבדל העיקרי הוא שב-object אפשר להגדיר מתודות ולדבר על visibility (אם זה אכן המצב, אז record הוא שריד ארכאי ולא יותר). אבל התיעוד גם מדבר במפורש על ירושה בהקשר של object — ואם יש כזו, אז זה שונה מה-struct של #C (שם יש משהו בכאילו — כל ה-struct-ים יורשים מ-ValueType, אבל אי אפשר לרשת הלאה).

  4. ik_5 מאת

    record כשמו הוא רשומה. ממש כמו במסדי נתונים. אני שומר בו מידע מאוגד אבל לא מזין לו רוטינות שונות. הוא רחוק מלהיות משהו ארכאי – יש בו שימוש.

    זה בדיוק ההבדל בין "גליון אלקטרוני" לבין "מסד נתונים". גליון אלקטרוני מאוד קרוב למסד נתונים, כי הם שניהם שומרים מידע, על שניהם אפשר לעשות מניפוליצה וכו', אבל הם עדיין שונים מאוד בתכלית שלהם.
    בפסקל ו בC השימוש לרשומות הן זהות. במקרה הזה אני חושב שדווקא #C בילבלו את העסק. ובבורלנד שמימשו את Delphi.NET גם הוסיפו את הבילבול הזה, כאשר כבר אין גבול מוגדר.

  5. שי

    טענת הארכאיות היא פשוטה: כל מה שאתה יכול לעשות עם record, אתה יכול לעשות עם object. עם object אתה יכול לעשות גם יותר. השימוש ב-object בתור record לא עולה לך בשימוש במשאבים (כי גם object הוא על ה-stack), מקסימום איזושהי עלות קוגניטיבית שקשורה יותר להרגלים שלך מאשר לתכונות של השפה.

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

    אם אני טועה, אשמח לקבל הסבר; אם אני לא טועה, אשמח לקבל הסבר אחר (למה עוד משתמשים ב-record).

  6. ik_5 מאת

    אקח כדוגמה את המבנה הבא:

    יש לך מבנה של קובץ שמוגדר בצורה הזו:
    בית ראשון מכיל שם
    עשרה בתים מכילים מיקום
    שלושה בתים מכילים גרסה
    תשע עשרה בתים מכילים מידע כללי
    255 בתים מכילים מידע ספציפי.

    עוד מבנה כזה למידע הבא בתור וככה זה חוזר עד לסוף הקובץ.

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

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

    סיבה נוספת לשימוש ברשומה היא רשימה מקושרת. אני כמובן שיכול להשתמש במחלקה ו/או אובקייט, אבל ההשקעה מבחינת הזכרון עולה על השימוש ברשומה.

    ואני יכול להמשיך בדוגמאות, אבל אני חושב שזה די ממוצה.

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

  7. שי

    א. למה אובייקט דורש "יותר מבחינת ניהול זכרון" מרשומה? לא אמרנו שהוא על המחסנית, וצמצום דרישות הזכרון (וזמני הביצוע — מעקב-מצביע אחד פחות) זו הסיבה העיקרית לקיומו?

    ב. אתה יכול לספר לי על התמיכה ברשומות בג'אווה? עבדתי בצורה די יסודית עם 1.4, ופחות יסודית עם 1.5, ולא נתקלתי בשום דבר כזה.

  8. ik_5 מאת

    א. אובייקט חייב אתחול לזכרון עם new ושיחרור הזכרון עם Dispose מה שעושה אותו "גמיש, מאוד. וכבר 2 דברים שאתה חייב לממש בלי קשר למבנה שלך.
    ברשומה אתה לא צריך לאתחל את הזכרון (אלא אם מדובר במצביע/ים) אז אין לך צורך בעוד תוספות. וגם אם יש אתחול זכרון הוא יהיה עם GetMem (או malloc ב C).

    ב.בקשר לג'אווה טעות שלי, זכרתי משהו לא נכון. אין sstruct. הדבר הכי קרוב זה interface

  9. Shai

    טוב, הכרחת אותי, אז הלכתי לבדוק בתיעוד.

    לפי http://freepascal.org/docs-html/ref/refch6.html, אובייקט לא חייב אתחול עם new. אתה יכול, אם אתה רוצה (כמו בדוגמה שלך), אבל אתה יכול להגדיר גם משתנה מסוג TMyObject ממש ולא PMyObject כמו בדוגמה שנתת, ואז לא צריך לאתחל אותו עם new. אם אתה משתמש רק באתחול עם new, אתה כמעט לא מרויח מהשימוש באובייקטים (בהשוואה למחלקות).

    אוקצור: כמו שטענתי, record לא נותן לך שום דבר שלא יכולת לעשות עם אובייקט. אם אתה רוצה, אתה יכול אפילו לארוז (pack) אובייקטים.

    ועוד טיפ: לפי http://freepascal.org/docs-html/ref/refse25.html (השורות שמיד מתחת לדיאגרמה), לא חייבים להגדיר יוצר והורס לאובייקט (חייבים רק אם משתמשים במתודות וירטואליות), ואפשר לתת להם איזה שמות שרוצים (השמות init ו-done שמופיעים שם הם רק דוגמה).

  10. ik_5 מאת

    1. יש הרבה דברים שלא כתובים בדוקומנטציה, אבל אם תשאל ברשימת התפוצה של freepascal או ב IRC תקבל עליהם תשובה.

    2. הנה חלק (מאוד קטן) של הגדרות שאתה יכול לעשות ברשומה, אבל לא במחלקה או אובייקט:
    http://freepascal.org/docs-html/ref/refsu15.html

    עוד דבר שאתה לא יכול לעשות עם אובייקט אבל כן עם record:

    TMyRecord = bitpacked record
    a : 1..3;
    b : 1..50;
    end;

    אתה לא יכול לגרום לobject להיות דחוס עי בסיס ביטים, כלומר שבראשון הוא יהיה 3 ביטים, ובשני 50 ביטים. באובייקט אתה רק יכול לגרום לשדות לקבל ערך מ1 עד שלוש או מאחד ועד חמישים.

    או במילים אחרות, ברשומות השליטה היא רק על השדות.

  11. פינגבק: מערך כתכונה | לראות שונה

כתיבת תגובה

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

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s