OOP ב-++C

inkydinky

New member
OOP ב-++C

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

לילךLilach

New member
פונקציה וירטואלית מממשת פולימורפיזם

כלומר: נניח שיש אב אחד ,ונניח שהוא בכלל מחלקה אבסטרקטית ,למשל יש לנו אב בשם shape שהוא לא צורה בפני עצמו ואין שום סיבה ליצור לו מופעים. אבל יש לו משתנים למשל x,y וכל מיני שיטות למשל: draw,move וכו..... אז רושמים את הפונקציות כוירטואליות בבנים : line ,circle,rectangle וכו. אחרי זה רוצים להחזיק מערך או וקטור של צורות שונות ,אז יוצרים מערך מסוג מצביעים ל shape ,כיון שאבא יכול להצביע על בן ,אז יכולים לשייך לכל מצביע צורה אחרת (כולם בנים של shape) וכאשר רוצים לעשות משהו לכל האיברים במערך ,למשל לצייר את כולם ,אז קוראים לפונקציה draw בלולאה שרצה על כל איברי המערך וכיון שהיא וירטואלית ,אז למרות שהמצביע הוא מטיפוס האב ,למעשה תודפס הפונקציה הספציפים של כל בן ,כי פונקציות וירטואליות ממוקמות ב vtable של הבן וככה התוכנית יודעת להפעיל את הפונקציה של הבן ולא של האב. להבדיל מפונקציה וירטואלית: אם יהיו באב פונקציות שלא יוגדרו וירטואליות אז בהרצה של פונקציה לא וירטואלית על אובייקט שמצביע אליו מצביע מטיפוס האב ,תופעל הפונקציה של האב ושלא של הבן.
 

inkydinky

New member
לא נראה לי שזה מה ששאלתי אבל זה...

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

yair24

Member
עד כמה שאני זוכר...

לפונקציה וירטואלית טהורה (=0) אין אפשרות ליקרוא. עכשיו בקשר לפונקציות האחרות זה תלוי בפוינטר. אם הפוינטר הוא של מחלקת האב אז הפונקציה של האב תיקרא. אבל אתה יכול לעשות CASTING למחלקת בן ואז תיקרא הפונקציה של הבן. דוגמא: CLASS BASE CLASS DERIVED :pUBLIC BASE בתוכנית אתה מגדיר משתנה: BASE *B1 ()B1->SHOW כאן תופעל הפונקתיה של הBASE. לעומת זאת:
(derived)b1->show​
כאן תופעל פונקצית הבן יאיר (אני מקווה שכל הסוגריים כאן יצאו נכון)
 

yair24

Member
והמקרה האחרון שאותו שכחתי...

derived *d1 d1->show()​
כאן תופעל פונקצית הבן. (DERIVED) יאיר
 

inkydinky

New member
תודה... הבנתי...

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

מתודות עוברות בירושה. אפשר "לדרוס" אותן בבנים כדי לממש אותן אחרת מאשר באב. לפעמים האב הוא מופשט, כלומר מכיל לפחות מתודה אחת שלא ממומשת. אם במקרה הוא מכיל גם מתודות ממומשות, אז הבנים יירשו אותן כרגיל. אתה יכול להסתכל על זה כהתנהגות ה-DEFAULT של הבנים. לדוגמה, אם נניח שכל האנשים יכולים לגרד באף, אז לא יהיה צורך לממש את הגירוד מחדש עבור כל SUBCLASS של PERSON. וזה לא משנה אם במקרה PERSON הוא מופשט. באופן כללי, נהוג ליצור מתודות וירטואליות. לכן, כאשר עובדים עם מצביעים ל-CLASS של האב (כמו במערך SHAPES), המתודה שתופעל תהיה של הבן. ישנם כמה מקרים יוצאי דופן, שבהן המתודה של האב תופעל: 1. הבנים לא "דרסו" אותה 2. היא לא וירטואלית 3. היתה בקשה מפורשת להפעיל אותה: SHAPE::DRAW. אם אתה רוצה את ההסבר הטכני לאיך זה קורה, אני מוכן להסביר...
 

לילךLilach

New member
מצטערת אבל אתה טועה ומטעה

אני אנסה להסביר שוב. פונקציה וירטואלית באה לאפשר מימוש של מנגנון שנקרא: פולימרפיזם. זהו מנגנון שבו למרות שהמצביע הוא של מחלקת האב (הבסיס) אז אם יש פונקציה שמוגדרת כוירטואלית (לא משנה אם טהורה או לא) אזי אם המצביע מצביע על מצביע של המחלקה הנגזרת (הבן) תופעל הפונקציה של הבן ולא של האב !!!!! המשמעות של פולימורפיזם ושימוש בפונקציות וירטואליות ,שלא צריך לעשות casting כי הקומפיילר יודע לבד להפעיל את הפונקציה של הבן ולא של האב וזה בגלל ה vtable של הבן שבה נשמרים מצביעים לפונקציות של הבן. הנה דוגמא: public class base } virtual public draw(){} { public class derive:public base } public draw(){} //אין צורך לרשום שוב virtualבבן זה אוטומטי וירטואלי { void main() } derive d; base *p; p=&d; p->draw() יפעיל את הפונקציה של הבן !!!!! ולא של האב כי זאת פונקציה שהוגדרה וירטואלית באב ,לא צריך קאסטינג !!!! וסליחה על הכיוונים ההפוכים ,מה לעשות שהפורום לא תומך בכתיבה משמאל לימין? { נ.ב. פונקציה וירטואלית טהורה גורמת לכך שהמחלקה נהיית אבסטרקטית כלומר לא ניתן ליצור אובייקטים של המחלקה ,רק מצביעים ורפרנס שיכולים להתביע על אובייקטים של בנים.
 

voguemaster

New member
רק דבר אחד..

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

inkydinky

New member
רגע...

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

שאקל

New member
פולומורפיזם, תורשה, אבסטרקטית

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

gilad_no

New member
לא בדיוק,

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

שאקל

New member
לא דורס שום דבר

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

חובבן

New member
אתה טועה ומטעה

class A { virtual void f() {}; } class B: public A { void f() { A::f(); }​
 

שאקל

New member
נו באמת. מה כתבת כאן בעצמך?

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

חובבן

New member
זהו שימוש נפוץ.

לעיתים תכופות יש מימוש defult פשוט לפונקציה הוירטואלית ב super class. לדוגמא, ה super class הוא רכב ו ה sub class הוא סובארו. פונקציית ה print של רכב יכולה להדפיס את מספר הגלגלים, בעוד פונקצית ה print של סובארו יכולה להדפיס פרטים יחודיים לדגם, ובנוסף את מספר הגלגלים (ע"י קריאה לפונקציה ב super ). אגב, ניתן לבצע אותו דבר גם ב pure virtual רק שאז לא ניתן לקרא ישירות לפונקציה מה super. נ.ב. מזל שמצאתי עבודה ב ++C לפני שנים רבות. אם היו רואים את התגובה שלך יתכן שהייתי נשאר מובטל.
 

yair24

Member
באמת שכחתי את השימוש הזה...

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

לילךLilach

New member
חוץ מזה לOOP עדיף לעבור ל JAVA

למה לשבור את הראש עם דיסטרקטורים וניקוי זיכרון שהוקצה דינמית? תעברו ל JAVA וזהו ,שפה OOP טהורה עם המון אפשרויות.
 

חובבן

New member
ל java יתרונות (וחסרונות) אבל

שפת OOP טהורה היא ודאי שלא. אם כבר התואר מתאים ל SmallTalk.
 

philips

New member
נכון....

Java היא מה שנקרא, שפת OOP בסבבה ובכיף
- חלק מעקרונות ה OOP הוקרבו על מזבח הכיף התכנותי..ואין כל בעייה בכך... כך גם Python....
 
למעלה