MiniMizer פורסם 2009 ביוני 19 מחבר Share פורסם 2009 ביוני 19 אתה צודק, קו המחשבה המנחה את התרגיל (ואת כל הקורס למעשה) הוא גנריות - לכתוב קוד גנרי עד כמה שניתן, על מנת לאפשר גמישות מירבית בעתיד, Cosmic Hierarchy אם תרצה.הקישור שסיפקת בהחלט מעניין (שמרתי אותו בצד ואני מתכוון לדון עם המרצה לגבי זה), אבל לא הבנתי את הנקודה הראשונה שרשמת (הנסיון שלי בתכנות ב-C++ מסתכם ב... אממ.. שלושה חודשים של קורס, בערך..). תוכל אולי להסביר למה התכוונת כשאמרת שעדיף לנצל את התכונות של C++ בכיוון של טיפוסים סטטיים חזקים? קישור לתוכן שתף באתרים אחרים More sharing options...
Zelig פורסם 2009 ביוני 19 Share פורסם 2009 ביוני 19 עדיף שתקרא על זה בצורה רצינית יותר, אבל הנה תקציר.דרך אחת לחלק את שפות התכנות היא לפי ההתייחסות לטיפוסים: בגדול אפשר לחלק שפות לשני סוגים: dynamically typed או statically typed.בשפות עם מערכות טיפוסים סטטיות לכל משתנה יש טיפוס ברור אשר ידוע באופן סטטי לקומפיילר בזמן קומפילציה (בירושה זה קצת יותר מורכב, אבל גם אז לקומפיילר ידוע טיפוס הבסיס). משתנה מטיפוס מסויים יכול להכיל רק ערכים מטיפוס זה (זו אחת ההגדרות של המונח "טיפוס").בשפות עם מערכות טיפוסים דינמיות הטיפוס של המשתנה לא ידוע בזמן קומפילציה, ולפעמים אף יכול להשתנות באמצע הקוד.חשוב מאוד להדגיש שלכל אחת מהשיטות חסרונות ויתרונות משלה, אבל היום אנחנו מדברים על C++.מערכת הטיפוסים של C++ היא סטטית והיא חזקה (strong typing, אם כי יש שפות יותר קפתדניות). פירוש הדבר זה שבזמן קומפילציה (וגם לרוב בזמן link) הקומפיילר יודע (בגדול) את הטיפוס של כל משתנה, כל פוקנציה, כל פרמטר וכל ערך, והוא אוכף זאת. היתרונות הן זיהוי שגיאות כבר בזמן קומפילציה, וכן ביצועים יותר טובים.זיהוי שגיאות: סוגים מסויימים של שגיאות לא יכולים לקרות. אתה לא יכול להציב אובייקט מסוג A לתוך משתנה מסוג B אם הם לא קשורים. יש לכך השלכות רחוקות: נניח שמימשת אובייקט שמכיל עטים (PenHolder). ב-C++ רשימה זו תכיל רק מצביעים לאובייקטים מסוג Pen או ליורשים ממנה. כל נסיון להכניס משהו אחר יגרום לשגיאת קומפילציה.אולם כאשר משתמשים ב-cosmic hierarchies (כלומר הרשימה מכילה מצביעים ל-Object שממנו כולם יורשים), אז אפשר להכניס כל סוג של אובייקט לרשימה! אפילו אם הוא ספינת מלחמה! (Battleship). זה כמובן בעיה. עכשיו כאשר עוברים על הרשימה צריך לוודא שכל האובייקטים הן Pen ולא משהו אחר.יתרון ביצועים: לדוגמא, כפי שהוסבר לעיל, לא צריך לוודא את הטיפוס בזמן מעבר על הרשימה. כמו כן יש עוד הזדמנויות לאופטימיזציה ע"י הקומפיילר. קריאה למתודות כאשר הטיפוס הסטטי ידוע היא יותר מהירה, לדוגמא. קישור לתוכן שתף באתרים אחרים More sharing options...
MiniMizer פורסם 2009 ביוני 19 מחבר Share פורסם 2009 ביוני 19 הבנתי את הדוגמה עם ספינת הקרב והעפרון, אולם היא לא ממש רלוונטית שלתרגיל שלפניי - הקונטיינר שלי הוא קונטיינר של Dish שהוא לא איזה אבא כללי של כל האובייקטים בעולם, אלא של בנים מאוד ספציפיים (צלחת, כוס ומזלג). מה שאני נדרש לעשות הוא לבדוק האם בן כלשהו שאני מקבל, זהה לאחד הבנים "הידועים" של האבא.גם אם יום אחד יבוא מישהו וייצור אובייקט Battleship שיורש מ-Dish, הוא יהיה Dish לכל דבר (כי Dish נותן לבנים שלו שדות ופונקציות ספציפיות שיש רק לכלי מטבח ולא לספינות מלחמה..). קישור לתוכן שתף באתרים אחרים More sharing options...
Zelig פורסם 2009 ביוני 20 Share פורסם 2009 ביוני 20 הבנתי את הדוגמה עם ספינת הקרב והעפרון, אולם היא לא ממש רלוונטית שלתרגיל שלפניי - הקונטיינר שלי הוא קונטיינר של Dish שהוא לא איזה אבא כללי של כל האובייקטים בעולם, אלא של בנים מאוד ספציפיים (צלחת, כוס ומזלג). מה שאני נדרש לעשות הוא לבדוק האם בן כלשהו שאני מקבל, זהה לאחד הבנים "הידועים" של האבא.גם אם יום אחד יבוא מישהו וייצור אובייקט Battleship שיורש מ-Dish, הוא יהיה Dish לכל דבר (כי Dish נותן לבנים שלו שדות ופונקציות ספציפיות שיש רק לכלי מטבח ולא לספינות מלחמה..).אתה מתבלבל, אולי כי הדגשתי את הדבר הלא נכון.הבעיתיות של מה שאני רואה פה זה לא בגלל שהוא ,cosmic hierarchy, כי הוא לא (אם כי נראה לי שאתם צועדים בכיוון, אילו זה היה תוכנה אמיתית ולא תרגיל). הבעיתיות היא השאלה המפורשת שאתה שואל אובייקט: מה הטיפוס שלך? האם אתה מזלג? האם אתה צלחת? וכו'. זו הבעיתיות שמשותפת גם לקוד כמו שאתה מדבר עליו, וגם ל-cosmic hierarchies.קודם כל, אני מקווה שברור לך לפחות אינטואיטיבית, שקוד של האבא לא אמור לדעת על הבנים, ואיזה בנים יש. זה עקרון פשוט וברגע שאתה רואה שהתכנון שלך מצריך ידע כזה, זה בד"כ סימן טוב לבעית תכנון בהירככיה.זה בד"כ נכון גם לכל קוד פולימורפי שמתעסק עם הרבה יורשים של המחלקה המקורית - גם הוא (לרוב) לא אמור לדעת איזה בנים יש למחלקה.אולי זה מופשט מדי ולכן אפרש עם דוגמא (של Scott Meyers):class Animal { ... };class Tiger : public Animal { ... };class Wombat : public Animal { ... };class Narwhal : public Animal { ... };void Nap(Animal& a){ if (Tiger* pt = dymanic_cast<Tiger*>(&a) ) pt->Sleep(7); else if (Wombat* pw = dymanic_cast<Wombat*>(&a) ) pw->Sleep(12); else if (Narwhal* pn = dymanic_cast<Narwhal*>(&a) ) pn->Sleep(8); else throw std::exception( "Unknown Animal"); }לקוד הזה כבר יש הרבה בעיות, ואפילו לא התחלנו:1) הוא עלול להיכשל! יש לו failure mode מיותר לחלוטין.2) על פניו קצת קשה לתחזק אותו - הוא ארוך ופחות קריא מקוד אלטרנטיבי (ראה למטה). 3) מה אם יש 30 טיפוסי חיות, במקום 3?4) הוא קצת איטי יותר ממה שהוא חייב להיות.אבל הבעיות הגדולות של השיטה מתחילות עכשיו! מה קורה אם מישהו מוסיף סוג חדש של חיה? הסיכוי שאותו מישהו יזכור לעדכן את הפונקציה Nap הוא נמוך: זה יכול להיות מישהו אחר בצוות שלך, מישהו אחר בצוות אחר בפרויקט, או אפילו לקוח שקיבל את הקוד שלך ויש לו את ה-headers אבל לא את ה-source. מזל טוב! הרגע יצרת באג חמור בקוד!עוד בעיה היא שכל קוד דומה צריך גם הוא להתחיל לבדוק את הטיפוסים. כלומר צריך לכתוב קוד "מה הטיפוס שלך" דומה עבור כל פעולה כמו Feed, Play.הבה נשווה את זה לקוד אלטרנטיבי:class Animal {public: virtual void Nap() = 0; virtual void Eat() = 0; virtual void Play() = 0;};class Tiger : public Animal { ... };class Wombat : public Animal { ... };class Narwhal : public Animal { ... };void Nap(Animal& a){ a.Nap();}void Eat(Animal& a){ a.Eat();}void Play(Animal& a){ a.Play();}אני ממליץ בחום לכל מי שקצת חשוב לו להיות מתכנת סביר ב-++C לקרוא את ++Effective C ואת ++More Effective C של Scott Meyers.ולמתכנת ++C שזה שזה לא חשוב לו, אני ממליץ בחום שיקפוץ ערום לבריכה מלאה תנינים, או שיתחיל להתעניין בנושא.(ההוראות ב-post זה נכתבו בלשון זכר, אך הן מיועדות גם לנקבות ולהרמפרודיטים). קישור לתוכן שתף באתרים אחרים More sharing options...
MiniMizer פורסם 2009 ביוני 20 מחבר Share פורסם 2009 ביוני 20 נכון, הבנתי את הבעיה, לשאול כל אובייקט "האם אתה X? האם אתה Y? האם אתה Z?" זו לא דרך נכונה.אם כך, *מהי* הדרך הנכונה ביותר להשוות בין סוגי טיפוסים בזמן ריצה, אם כל מה שאני צריך לקבל בחזרה זה ערך TRUE/FALSE ? קישור לתוכן שתף באתרים אחרים More sharing options...
Zelig פורסם 2009 ביוני 20 Share פורסם 2009 ביוני 20 נכון, הבנתי את הבעיה, לשאול כל אובייקט "האם אתה X? האם אתה Y? האם אתה Z?" זו לא דרך נכונה.אם כך, *מהי* הדרך הנכונה ביותר להשוות בין סוגי טיפוסים בזמן ריצה, אם כל מה שאני צריך לקבל בחזרה זה ערך TRUE/FALSE ?(תקרא את ה-post המעודכן)הדרך ה"נכונה" (ותמיד יש מקרים שהיא דווקא לא נכונה) היא לתכנן את התוכנה ככה שלא צריך לשאול את השאלה הזו.אם אתה ממש חייב לשאול אותה, בד"כ שאלה פשוטה על טיפוס ספציפי (כלומר כן/לא) עדיפה:// Badvoid FlyMeToTheMoon1(Animal& a){ if (Wombat* pw = dymanic_cast<Wombat*>(&a) ) pw->Fly( TO_THE_MOON ); else if (Parrot* ppa = dymanic_cast<Parrot*>(&a) ) ppa->Fly( TO_THE_MOON ); else if (Pterodactyl* ppt = dymanic_cast<Pterodactyl*>(&a) ) ppt->Fly( TO_THE_MOON ); else throw std::Exception("Can't fly");}// Bettervoid FlyMeToTheMoon2(Animal& a){ if (FlyingAnimal* pf = dymanic_cast<FlyingAnimal*>(&a) ) pf->Fly( TO_THE_MOON ); else throw std::Exception("Can't fly");} קישור לתוכן שתף באתרים אחרים More sharing options...
MiniMizer פורסם 2009 ביוני 20 מחבר Share פורסם 2009 ביוני 20 אוקי, הבנתי, תודה רבה בכל מקרה, נתקלתי בעוד בעיה במימוש - כאשר אני משווה אובייקט אחד למשנהו, אני צריך להשוות לא רק את הטיפוס אלא גם פרמטרים אחרים. כלומר, אם יש לי אובייקט מסוג צלחת - ואני צריך לוודא שהוא "מתאים" למכונה שלי, אני צריך לבדוק שגם הגובה, הצבע, החומר וכו' שלו מתאימים לי - ולא רק שזו אכן צלחת. הבעיה היא שכל הכלים יורשים מאב משותף שנותן להם רק שני שדות - צבע וחומר, ואני לא יכול לכתוב פונקציה שהיא Pure Virtual ומקבלת פוינטר לאובייקט האב, משום שהיא נדרשת להשוות שדות שלא קיימים באב והקוד לא מתקמפל. איך אפשר לפתור את הבעיה הזאת? הדרך היחידה שחשבתי עליה היא לבנות מערכת של if-ים (למשל, אם האובייקט שלי הוא מטיפוס בן א', אז תקרא לפונקציה שמקבלת פוינטר לבן א' וכו'), אבל זה פתרון ממש גרוע וחייב להיות פתרון אלגנטי יותר. קישור לתוכן שתף באתרים אחרים More sharing options...
Zelig פורסם 2009 ביוני 20 Share פורסם 2009 ביוני 20 נכון. זה לא אלגנטי, ואני רואה שאתה מפתח אינטואיציה בריאה!כתיבת operator== בנוכחות פולימורפיזם זו בעיה ידועה, גם ב-JAVA. יש כל מני פתרונות. תלוי איזה סוג של השוואה אתה מחפש.מאחר שאתה רוצה גם לוודא את הטיפוס, typeid יעזור לך ( http://msdn.microsoft.com/en-us/library/fyf39xec(VS.80).aspx ).מזמן לא עשיתי את זה, אבל הייתי עושה משהו כמו שילוב של הרעיון הזה: http://stackoverflow.com/questions/565765/implementing-operator-when-using-inheritance/565999#565999יחד עם הרעיון הזה: http://stackoverflow.com/questions/565765/implementing-operator-when-using-inheritance/565784#565784Class Animal{public: bool operator==(const Animal& rhs) const;protected: virtual bool implementIsEqual(const Animal& a) const = 0;};bool operator==(const Animal& rhs) const{ if ( typeid(this) != typeid(&rhs) ) return false; // different concrete types return implementIsEqual(rhs);}לא ממש בדקתי את כל ההשלכות של הקוד אבל נראה לי שזה יפעל.עריכה: עוד גישה : http://artis.imag.fr/~Xavier.Decoret/resources/C++/operator==.html קישור לתוכן שתף באתרים אחרים More sharing options...
MiniMizer פורסם 2009 ביוני 20 מחבר Share פורסם 2009 ביוני 20 אז זהו, שאני לא בטוח שזה יפעל. קודם כל אין לי אובייקטים של ממש אלא רק פוינטרים לאובייקטים, לכן ה-implementIsEqual תצטרך לקבל פוינטר ולא רפרנס - סביר להניח שאם הייתי יכול לעבוד עם רפרנסים זה היה חוסך לי את כל הכאב ראש הזה, אבל זה מה יש :-\.שנית כל, היא pure virtual, משמע בכל בן אני אצטרך לממש אותה באופן שונה - וכאן אני שוב מגיע לאותה נקודה שבה אני תקוע; אם נמשיך עם הדוגמה שלך:class Animal{string color;}class Leopard: public Animal{bool carnivore;}class Cow: public Animal{int weight;}ב-Lion הפונקציה implementIsEqual תקבל פוינטר ל-Animal תדרש לבדוק האם האובייקט שקיבלתי הוא גם Carnivore וב-Cow הפונקציה תקבל פוינטר ל-Animal תדרש לבדוק האם המשקל זהה... אבל זה פשוט לא יתקמפל, כי ב-Animal אין שדות של Carnivore ו-weight :-\ קישור לתוכן שתף באתרים אחרים More sharing options...
MiniMizer פורסם 2009 ביוני 20 מחבר Share פורסם 2009 ביוני 20 עכשיו כשאני חושב על זה, יכול להיות שזה יעבוד אם אני אצור פונקציות וירטואליות בכל בן שמקבלות פוינטר לאבא ורק בודקות את הטיפוס. אם הטיפוס לא תואם, היא תחזיר ישר false ונגמר הסיפור. אם הטיפוס תואם, היא תעביר את הפוינטר הלאה לפונקציה פנימית בתור פוינטר של בן, והפונקציה הפנימית כבר תבצע את ההשוואה. בוא נראה אם זה יעבוד קישור לתוכן שתף באתרים אחרים More sharing options...
MiniMizer פורסם 2009 ביוני 20 מחבר Share פורסם 2009 ביוני 20 כך נראה הקוד שלי (רעיונית):bool Main_Compare(Father * object) {if (type == object.type) {return Specific_Compare(object);else return false;}bool Specific_Compare(Son * object) {if (...) return true;else return false;}הבעיה שלי (של הקומפיילר ליתר דיוק) היא בהמרה של הפוינטרים, הוא צועק שזה invalid conversion (ואני נאלץ להאמין לו).אז קודם כל, האם פתרון כזה יעבוד ברמה התאורטית? ואם כן, איך אני צריך לבצע את ההמרה?\\עריכה: התגברתי על ההמרה באמצעות dynamic_cast. עודנה נשאלת השאלה האם קונספט זה יעבוד? קישור לתוכן שתף באתרים אחרים More sharing options...
Zelig פורסם 2009 ביוני 20 Share פורסם 2009 ביוני 20 אני לא בטוח לאן אתה הולך עם כל זה, ואין לי הרבה זמן לברר, אבל:א) אם יש לך פוינטר ל-X ואתה רוצה רפרנס, פשוט תעשה *ב) זה ברור שלכל בן צריך לממש את implementIsEqual. זה כל הרעיון! יתכן שגם תצטרך לממש מחדש את operator== לכל בן, אני לא מאה אחוז בטוח.ג) המרה של אבא לבן היא המרה מסוכנת שבעקרון חייבת להיעשות עם dynamic_cast, ולכן הקומפיילר צועק. אחת המטרות העיקריות של מה שכתבתי כאן היא להמנע מכך! ד) בקוד שלך ב-Main_Compare אתה שוב גורם לקוד של האבא להכיר את הבן (Son מופיע בחתימה של Specific_Compare).אם תחשוב על זה, אתה תראה שהקוד שהצעתי למעלה עושה מה שאתה מנסה לעשות אבל בצורה בטוחה יותר:1) המשתמש קורא ל-Animal::operator== (שזה אצלך כמו Main_Compare). י2) האב מוודא ששני הצדדים הם מאותו טיפוס ע"י האופרטור typeid. שני אובייקטים שאינם מאותו טיפוס בהכרח לא שווים (בהגדרה שאני משתמש בה, יש אחרות).3) האב קורא לפונקציית ההשוואה המתאימה לבן פשוט ע"י קריאה לפונקציה וירטואלית! (שזה אצלך כמו בדיקת ה-type וקריאה ל-Specific_Compare). קישור לתוכן שתף באתרים אחרים More sharing options...
MiniMizer פורסם 2009 ביוני 20 מחבר Share פורסם 2009 ביוני 20 הבנתי למה אתה מתכוון (ותודה ל-The_Coolest ו-SoapSeller שתרמו להבנה ) והצלחתי ליישם את זה בצורה שהצגת. תודה רבה! קישור לתוכן שתף באתרים אחרים More sharing options...
Zelig פורסם 2009 ביוני 20 Share פורסם 2009 ביוני 20 בהצלחה.אל תשכח לקרוא (תפנה זמן אם צריך) את הלינק שנתתי ואת הספרים (שהם די קצרים תכלס). ההסברים והדוגמאות שם הרבה יותר טובים. קישור לתוכן שתף באתרים אחרים More sharing options...
Rin פורסם 2009 ביוני 20 Share פורסם 2009 ביוני 20 יש מה שנקראRTTIRun-Time Type Informationזה בעקרון מחזיר מחרוזת עם שם המחלקהGOOGLE IT קישור לתוכן שתף באתרים אחרים More sharing options...
Recommended Posts
ארכיון
דיון זה הועבר לארכיון ולא ניתן להוסיף בו תגובות חדשות.