עבור לתוכן

C++ ו-OOP: שימוש בירושה פרטית / האם מחסנית היא רשימה

Featured Replies

פורסם

SDF זה זה שהגיב מעלי :silly:

זה שגוי. מחסנית לא מכילה רשימת אלא היא סוג של רשימה. לכן צריך להשתמש בירושה.

כמו שאני רואה את זה, יש כאן למעשה שתי שגיאות נפוצות:

1) האם מחסנית היא רשימה? (stack is-a list?)

נסתכל על פעולות והתנהגות (כלומר ממשק) נפוצות על רשימה:

הכנס איבר לראש הרשימה (push_front)

הכנס איבר לסוף הרשימה (push_back).

מחק איבר כלשהו מרשימה.

עבור על האיברים ברשימה.

מצד שני נסתכל על ממשק של מחסנית:

הכנס איבר לראש. (push_front)

הסתכל על האיבר בראש (peek)

הוצא את האיבר מהראש (pop_front).

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

לא ניתן להשתמש במחסנית בכל מקום שמקבל רשימה, ולכן מחסנית אינה רשימה.

ב-C++: מחסנית לא יורשת באופן פומבי מרשימה.

2) ב-C++: אם מחסנית ממומשת ע"י רשימה, האם ניתן לרשת באופן פרטי (private inheritance)?

כידוע, היחס IS-IMPLEMENTED-IN-TERMS-OF ניתן למדל ב-C++ בשתי דרכים: private/protected inheritance או ע"י הכלה (אובייקט stack מכיל משתנה מסוג list).

הדיון הוא ארוך ולכן על מנת לקצר אציע את הכלל הבא:

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

מידע נוסף ניתן למצוא ב: http://www.gotw.ca/publications/mill06.htm

פורסם

עוד הערה:

ב-STL קיימים שלושה סוגים בסיסיים של "רשימות דינאמיות":

List - רשימה מקושרת (דו-מקושרת, ליתר דיוק), כפי שמלמדים בתיכון.

היתרון שלה - הוספת והסרת איברים מאמצע הרשימה ביעילות.

החסרון - גישה לאיברים לא יעילה (חייבת להיעשות בצורה סדרתית).

Vector - מערך דינאמי, כלומר מערך שאם מבקשים להוסיף לו איברים הוא גדל.

היתרון - פשטות ויעילות. בגדול זה שקול למערך שניתן להגדיל אותו.

Deque - תור דו-צדדי. זה בעצם Vector, רק שאפשר להוסיף איברים בתחילתו ב-O(1).

שלושת הטיפוסים הללו מממשים את כל הפעולות, כולל הוספת והסרת איברים בתחילת/אמצע/סוף הרשימה, אבל לכל אחד יש את היתרונות שלו על פני האחרים.

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

פורסם

1) אני מסכים, לא טענתי זאת.

2) לא הבנתי את ההסבר(אולי מכיוון שלא הסברת). אנא הרחב.

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

פורסם

הממ לא ברור לי מה לא ניסיתי להסביר או שלא הבנת.... :)

וכמובן לא ניסיתי לסתור את דבריך, רק להוסיף (למקרה שמישהו חשב אחרת).

וזה גם לא קשור לירושה (ב-STL, או ספציפית ב-containers, אין כמעט שימוש בירושה, אלא בעיקר ב-Templates)

פורסם

דיברתי לזליג.

פורסם
  • מחבר

1) אני מסכים, לא טענתי זאת.

2) לא הבנתי את ההסבר(אולי מכיוון שלא הסברת). אנא הרחב.

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

הנקודה העיקרית לכל העניין היא צמצום coupling - כלומר צמצום התלות הדדית בין רכיבי תוכנה שונים.

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

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

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

ארכיון

דיון זה הועבר לארכיון ולא ניתן להוסיף בו תגובות חדשות.

דיונים חדשים