10 באפריל 2013 יוםרביעי ) 236703 ( עצמים מונחה תכנות ב' מועד 3102-2012, חורף סמסטר סמסטר סוף מבחן כהן טל מרצה: כרמלי עמית מושקוביץ, אייל מתרגלים: הנחיות: השאלות. כל על לענות עליכם משקל. שוות שאלות 5 במבחן מהנקודות. 15% ותקבלו יודע/ת" "לא כתבו התשובה, את יודעים אינכם אם שאלה, בכל ריצה בזמן נכשל או מתקמפל, לא הקוד כי ייתכן הקוד, פלט את לציין נדרש בה שאלה בכל ולהסביר הריצה זמן או הקומפילציה שגיאת את לציין יש זה במקרה פלט. לייצר במקום במפורש. זאת לציין יש ריצה זמן שגיאת יש ואחריו פלט קיים אם מתרחשת. היא מדוע התשובה, על הניתן בציון תפגע נכון( הוא אם )אף בעליל רלוונטי לא מידע הוספת לעניין. ענו שלילי. ניקוד למתן אף קיצוניים ובמקרים ברור. בכתב תשובתכם כתבו חדש. בעמוד תשובה כל התחילו אלקטרוני. לא עזר חומר בכל להשתמש מותר כולם. את שקיבלתם כעת וודאו ) זה עמוד כולל ( עמודים 6 במבחן שעות. שלוש הבחינה משך.3.4.5.7.6.8.9 בהצלחה!! 2102-2102 חורף 1 227612 עצמים מונחה תכנות
שאלה - 0 Templates ב- C++ 31( נק'( נתון קטע קוד בשפת ++C המתאר עץ-בינארי של טיפוסים. struct Nil { template <typename D, typename L, typename R> struct BinaryTree { typedef D Data; typedef L Left; typedef R Right; template <typename D> struct BinaryTree<D,Nil,Nil> { עץ-בינארי של טיפוסים הנראה כך: א. באמצעות קטע הקוד הגדירו ב-++ C int char int float char ב. נתון קטע קוד חלקי אשר מקבל כפרמטר עץ-בינארי של טיפוסים ומחזיר את העומק שלו. השלימו את קטע הקוד במחברת התשובות. template <typename Tree> struct Height { enum { height = 1 + ( <1>? <2> : <3> ) template <> struct Height<Nil> { enum { height = <4> כתוב קוד באמצעות ++C Templates המקבל עץ בינארי של טיפוסים, וטיפוס נוסף, ומחזיר 0 אם הטיפוס הנוסף קיים בעץ הבינארי של הטיפוסים, ו- 1 אחרת. ניתן להשתמש בטיפוס :SameType template <typename X, typename Y> struct SameType { enum { value = 0 template <typename X> struct SameType<X,X> { enum { value = 1 תכנות מונחה עצמים 2 227612
שאלה - 3 מחלקות מקוננות בשפת )31 Java נק'(: נתון קטע הקוד הבא )קטע קוד זה עובר קומפילציה(: public class A { public void f(){ System.out.println("A.f()"); } public static class B extends A { public void f() { System.out.println("B.f()"); } } public B g(){ class C extends B{ public B g(){ return new C(){ public void f(){ System.out.println("D.f()"); } } //C.g public void f(){ System.out.println("C.f()"); } //C return new C(); } //A.g }//A א. ב. כמה קבצי class יווצרו כתוצאה מתהליך הקומפילציה של קטע הקוד? עבור כל אחת מן השורות הבאות, הסבירו מה יהיה הפלט: 1. (new A()).f(); 2. (new A.B()).f(); 3. (new A.B()).g().f(); 4. (new A.B()).g().g().f(); 5. (new A.B()).g().g().g().f(); משנים את הפונקציה g במחלקה A באופן הבא )השינויים מודגשים בקו תחתון(: public B g(){ class C extends B{ int y = 7; public B g() { return new C() { public void f() { System.out.println(y + 4); } } //C.g public void f(){ System.out.println(y + 3); } //C return new C(); } //A.g 1. (new A.B()).g().f(); 2. (new A.B()).g().g().f(); שימו לב! המשך השאלה בעמוד הבא! עבור כל אחת מן השורות הבאות, הסבירו מה יהיה הפלט: תכנות מונחה עצמים 3 227612
ד. בהמשך לסעיף הקודם כעת משנים את משנים את הפונקציה g במחלקה A באופן הבא )השינויים מודגשים בקו תחתון(: public B g(){ class C extends B{ int y = 7; public B g() { int Z = 17; return new C() { public void f() { System.out.println(Z + 4); } } //C.g public void f(){ System.out.println(y + 3); } //C return new C(); } //A.g עבור כל אחת מן השורות הבאות, הסבירו מה יהיה הפלט: 3. (new A.B()).g().f(); 4. (new A.B()).g().g().f(); תכנות מונחה עצמים 4 227612
שאלה - 2 Java Bytecode - ו- 31( Clousers נק'( א. כפי שנלמד בתרגולים שפת הביניים של Java ה- Bytecode- - עובדת בסמנטיקת מחסנית semantics(.)stack הסבירו בקצרה כיצד באה לידי ביטוי עובדה זאת בסינטקס )ה- של ה- Bytecode. )instruction set ב. כידוע, ב- Java מבצעים בדיקות טיפוסים בזמן קומפילציה. מדוע יש צורך בבדיקת טיפוסים גם בזמן טעינת הקוד המקומפל? )אנא רשמו בקצרה את הסיבה החשובה ביותר(. כיצד מסוגל ה- JVM למנוע?Stack Underflow הסעיף הבא עוסק ב- Closures ב- Java : ד. כידוע, בגרסה הנוכחית של Java אין,Closures 0. מהו המבנה הסינטקטי הקרוב ביותר אליו הקיים כיום ב- Java? 2. האם מבנה זה של השפה משתקף בתכנון ה- JVM? כלומר, האם קיימים מבנים מיוחדים ו/או פקודות מיוחדות אשר משמשים מבנה זה ב- Bytecode של?Java אם כן, אלו מבנים ו/או פקודות מדובר? אם לא, כיצד ממומש מנגנון זה ב- JVM? 2. הסבירו את ההבדל הבולט ביותר בין המימוש של מבנה זה ב- Java לבין המימוש של ב- Smalltalk. Closures תכנות מונחה עצמים 5 227612
שאלה - 4 Squeak 31( נק'( ברוב שפות התכנות מונחה עצמים, מתודה וירטואלית במחלקה עוברת גם למחלקת הבן היורשת ממנה. מחלקת הבן רשאית לדרוס מתודה כזו, אך לא להסירה. בשאלה זאת נגדיר סוג אחר של מתודה, "מתודה ידנית". מתודה ידנית היא מתודה אשר לא נורשת אוטומטית ע"י מחלקת הבן, אלא רק אם מציינים במפורש במחלקת הבן כי היא נורשת. אם לא מציינים, המתודה מוסרת ממחלקת הבן )כלומר לא ניתן להפעילה על ידי מחלקת הבן(. אם מתודה ידנית נורשת על-ידי מחלקת הבן, אפשר גם לדרוס אותה. א. נתונות המחלקות A ו- B אשר יורשת מ- A )הקוד עובר קומפילציה(: Object subclass: #A instancevariablenames: '' classvariablenames: '' bar "@ManualMethod" Transcript show: 'A bar'; cr. foo "@ManualMethod" Transcript show: 'A foo'; cr. A subclass: #B inheritedmanualmethods: 'bar' instancevariablenames: '' classvariablenames: '' bar "@ManualMethod" Transcript show: 'B bar'; cr. 0. מה יהיה הפלט עבור הקוד הבא: B(? new) bar 2. מה יהיה הפלט עבור הקוד הבא: B(? new) foo האם סוג המתודות הנ"ל סותר את עיקרון התאימות? הסבירו בקצרה. תארו כיצד ניתן לממש מנגנון כזה ב- smalltalk. ממשו את המתודה canunderstand: selector אשר מוגדרת במחלקה Behavior המתאימה לסכמת הפתרון שלכם. ב. ד. תכנות מונחה עצמים 6 227612
שאלה - 5 מבנה האובייקט ב- C ++ )31 נק'( א. נתון הקוד הבא: A* a = new A(); B* b = (B) a; כשתורגם לאסמבלי על-ידי מהדר כלשהו, התוצאה הייתה קוד כזה )מוצג כקוד ב- C לנוחות הקריאה(: void* a =... // allocate memory, invoke A() constructor, etc. void* b = a + sizeof(void*); כידוע בשפת אסמבלי אין טיפוסים, לכן כל המצביעים הם.void* בנוסף, לכל המצביעים יש גודל זהה, לכן sizeof(void*) הוא גודל של מצביע כלשהו. מה ניתן להסיק מקוד זה על המחלקות A ו- B? האם המהדר עובד ב- style GNU או,Borland style או שלא ניתן לדעת? נמקו בקצרה..1 ב. אם משתמשים ב- dynamic_cast, מתבצעת בדיקה כדי לקבוע האם ניתן לבצע המרה מהאובייקט הנתון לטיפוס הרצוי. למשל, בהינתן הקוד הבא: X* x = getsomexpointerfromsomewhere(); Y* y = dynamic_cast<y*>(x); ידוע שהמחלקה X אינה יורשת מאף מחלקה אחרת. כמו כן ידוע שהקוד נמצא מחוץ למחלקות X או Y )למשל, בפונקציה גלובלית כלשהיא(. אילו בדיקות )אם בכלל( מתבצעות בזמן קומפילציה כשהמהדר מטפל בשורה השנייה? אילו בדיקות )אם בכלל( מתבצעות בזמן ריצה על-ידי הקוד שהמהדר מייצר עבור השורה השנייה? האם התשובה לסעיפים הקודמים הייתה משתנה, אם ידוע שהקוד הנ"ל נמצא בבנאי X? של מחלקה )constructor( תכנות מונחה עצמים 7 227612
פתרון רשמי שאלה )31 0 נק'( בשאלה זו ניתן ניקוד חלקי כמעט בכל תשובה אפשרית. א. )4 נקודות( ישנן מספר תשובות אפשריות, כולן קיבלו את מלוא הנקודות. הרעיון היה להגדיר משתנה בשפת ה- templates של ++C באמצעות,typedef ולא משתנה רגיל. typedef BinaryTree<int, BinaryTree<char>, BinaryTree<int, BinaryTree<float>, BinaryTree<char> > > atree; ב. )8 נקודות( שימו לב לשימוש ב- typename. הוא חיוני כאשר רוצים לציין שביטוי מסוים בשפת ++C מייצג טיפוס, ולא שםשל משתנה או של מתודה. כמו כן, Height< ולא של ערך, ולכן צריך לגשת ל-,struct הוא שם של Height. >::height כמעט כל הסטודנטים קיבלו כאן לפחות ניקוד חלקי. template <typename Tree> struct Height { enum { height = 1 + ( Height<typename Tree::Left>::height > Height<typename Tree::Right>::height? Height<typename Tree::Left>::height : Height<typename Tree::Left>::height ) template <> struct Height<Nil> { enum { height = 1 )8 נקודות( ניתן לממש את הקוד בדומה לסעיף ב. template <typename Tree, typename T> struct Find { enum { found = SameType<T, typename T::Data>::value Find<typename T::Left, T>::found Find<typename T::Right, T>::found template <> struct Find<Nil> { enum { found = 1 שימו לב לשימוש ב-. אפשר להשתמש בכל זאת באופרטור הטרינארי, או בוריאציות אחרות, אבל לא ניתן להשתמש ב- if או ב- max, אלו פונקציות אשר מחושבות בזמן ריצה. סעיף זה נבדק ברחמנות עקב הדימיון לסעיף הקודם. שימו לב שיש להתייחס למקרה שבו העץ ריק. תכנות מונחה עצמים 8 227612
שאלה )31 3 נק'( בשאלה זו ניתן ניקוד חלקי כמעט בכל תשובה אפשרית. א. )5 נקודות( כפי שנלמד בתרגול, לכל מחלקה, כולל מחלקה מקוננת ומחלקות אנונימיות בפרט, נוצר קובץ,.class ולכן יווצרו ארבעה קבצי..class )5 נקודות( כמעט כל הסטודנטים הצליחו לפתור סעיף זה ללא קושי רב. זכרו כי יש קשרי ירושה בין המחלקות ולכן הן יורשות מתודות אחת מהשנייה. סטודנטים שטענו שקיימת שגיאת קומפילציה קיבלו ניקוד חלקי. 1. A.f 2. B.f 3. C.f 4. D.f 5. D.f )4 נקודות( המשתנה y הוא משתנה מופע )attribute( של המחלקה C, ולכן ניתן לגשת אליו ממחלקות מקוננות, כל עוד הן לא מקוננות במתודה סטטית )או שהן מחלקות מקוננות סטטיות(. 0. 10 2. 11 )6 נקודות( המשתנה z הוא משתנה לוקאלי במתודה g. לכן, מחלקות לוקאליות ואנונימיות יכולות לגשת אליו רק אם הוא מוגדר.final הוא אינו מוגדר,final ולכן תתרחש שגיאת קומפילציה. ב. ד. תכנות מונחה עצמים 9 227612
שאלה )02 3 נק'( א. )4 נק'( סמנטיקת המחסנית מתבטאת בסינטקס של ה- Bytecode בדרך הבאה: אין רגיסטרים אלא רק מחסנית. כל פעולות החשבוניות עובדות על התאים העליונים של המחסנית כאשר התוצאה נדחפת לתא הראשון של המחסנית. )4 נק'( הסיבה המרכזית היא שלא ניתן לדעת מאיפה הגיע קובץ ה- class או האם שונה ע"י מישהו לאחר הקומפילציה. )4 נק'( מכיוון שה- bytecode הוא typed ניתן לדעת תמיד כמה תאים במחסנית תופסת או מפנה פעולה שמבצעים בתוכנית. בנוסף מניחים כי בתחילת כל קטע קוד ידוע גובה המחסנית )לדוגמא בתחילת התוכנית גובה המחסנית הוא 1(. לכן צריך לוודא שלכל פעולת store למחסנית קיימת פעולת load מתאימה בקטע הקוד שנבדק. הערה: בסעיף זה נדרשתם להראות יצירתיות בנוסף לידע ולרשום את הרעיון שלכם לביצוע בדיקה זאת. בנוסף ניתן ניקוד חלקי ומלא כתלות באיכות הפתרון שהוצע. )8 נק'( תשובות: ב. ד. )0 נק'( המבנה הסינטקטי הוא local-class או.anonymous class )3 נק'( המבנה לא משתקף בתכנון ה- JVM. כל העבודה מתבצעת ע"י הקומפיילר אשר שם כל מחלקה בק בץו class משלה ומוסיף שדות סינטטיים למחלקה המקוננת המאפשר לה לגשת הן לאובייקט של המחלקה המכילה והן למשתנים הלוקאליים במתודה בה הוגדר. )3 נק'( ישנם כמה הבדלים: מחלקות אנונימיות ומקומיות הן מחלקות ולכן ניתן להגדיר בהן שדות ומתודות נוספות כמו כן ניתן לממש בהן ממשקים ולרשת ממחלקות אחרות. Blocks ב- smalltalk מאפשרים גישה גם למשתנים שהם לא.final הסינטקס של blocks ב- smalltalk הוא יותר פשוט ומכיל פחות.boilerplate code.i.ii.iii תכנות מונחה עצמים 10 227612
שאלה )31 4 נק'( א. )4 נק'( הפלטים הם:.B bar שגיאת זמן ריצה.does not understand )4 נק'( סותר את עיקרון התאימות. )5 נק'( בסעיף זה נדרשתם לתאר איך המנגנון מפעיל הן מתודות רגילות והן מתודות ידניות. )7 נק'( בסעיף זה נדרשתם לכתוב את הקוד של canunderstand אשר מתאים לתיאור המנגנון שניתן בסעיף הקודם. הערה: ב. ד. מי שלא כתב קוד קיבל ניקוד חלקי ביותר. מי שהניח הנחות מקלות מידיי אשר לא השאירו לו לממש כמעט כלום קיבל ניקוד חלקי בלבד. תכנות מונחה עצמים 11 227612
שאלה )02 5 נק'( א. הפתרון: המחלקה A יורשת מהמחלקה B. במחלקה B אין מתודות וירטואליות )ולכן אין,)vptr ובמחלקה A נוספו מתודות כאלה. שורת הקוד השנייה )תיקון הפוינטר( היא כדי לדלג על ה- vptr ולהגיע אל תחילת התת אובייקט של B אשר בתוך A. )3 נק' על הירושה, 3 נק' על קיום/אי-קיום מתודות וירטואליות(. פתרון נוסף שהתקבל, למרות שאינו מדויק: המחלקה A יורשת בירושה מרובה מ- A ומ- C. במקרה כזה, התוספת בשורת הקוד השנייה הייתה צריכה להיות,sizeof(C) אולם אולי C מכיל רק פוינטר. סגנון בורלנד )בסגנון GNU לא היה צורך בדילוג על ה- vptr משום שמבנה האובייקט היה שונה(. )4 נק'( פתרון: ב. היות שידוע ש- X לא יורשת ממחלקה אחרת, ניתן להסיק שה- cast הוא אכן יורשת מ- X, Y הבדיקה שמבצע המהדר היא שמחלקה.downcast ישירות או בעקיפין. )4 נק'( המהדר בודק אם הטיפוס הדינאמי של x )המשתנה( הוא Y* )מחוון ל- Y ( או מחוון לתת-מחלקה של Y.)4 נק'( שום שינוי. )0 נק'(. השאלה לא דרשה הסבר, אבל רבים מכם התעקשו להכשיל את עצמם. אמנם הקישור בתוך constructor הוא סטטי, אבל אין כאן כל שימוש במתודות וירטואליות. גם אין כל בעיה של רקורסיה אינסופית, משום שאין שום התחייבות שהפונקציה getsomexpointerfromsomewhere משתמשת באותו או בעקיפין( -- ייתכן בהחלט שימוש ב- constructor )ישירות X של constructor אחר. תכנות מונחה עצמים 12 227612