יוםשישי 14 בספטמבר 2012 ) תכנות מונחה עצמים ( 236703 מבחן סוף סמסטר סמסטר אביב 2012, מועד ב' מרצה: קרן לנץ מתרגלים: אייל מושקוביץ, ערן גלעד, ניר האוזר הנחיות: במבחן 5 שאלות שוות משקל. עליכם לענות על כל השאלות. במבחן שאלת בונוס. בכל שאלה, למעט שאלת הבונוס, אם אינכם יודעים את התשובה, כתבו "לא יודע/ת" ותקבלו 20% מהנקודות. בכל שאלה בה נדרש לציין את פלט הקוד, ייתכן כי הקוד לא מתקמפל. במקרה זה יש לציין את שגיאת הקומפילציה או זמן הריצה ולהסביר מדוע היא מתרחשת. ענו לעניין. הוספת מידע לא רלוונטי בעליל )אף אם הוא נכון( תפגע בציון הניתן על התשובה, ובמקרים קיצוניים אף למתן שלילי. כתבו תשובתכם בכתב ברור. התחילו כל תשובה בעמוד חדש. אין להשתמש בכל חומר עזר. במבחן 01 עמודים ( כולל עמוד זה ) וודאו כעת שקיבלתם את כולם..0.4.5.7.6.8.9 01. משך הבחינה שלוש שעות. בהצלחה!! תכנות מונחה עצמים 1 227612
.0 שאלה 1 Generics ב- C++ ו- Java 02( נק'( :T2 להלן מימוש של פונקציה ב-++ C הבודקת האם ניתן להציב מופע של טיפוס T1 למשתנה מטיפוס template<typename T1, typename T2> bool canassign(t1 from, T2 to) { T2* t2 = new T1(); return true; האם הפונקציה מבצעת את יעודה? אם כן, האם היא מטילה אילוצים נוספים על T1 ו- T2? מנה אילוצים אלו. אם לא, הסבר מדוע. 2. להלן הצעה למימוש דומה באמצעות :template specialization template<class T1, class T2> struct canassign { enum { result = false ; ; template<class T1> struct canassign<t1,t1> { enum { result = true ; ; מהי תוצאת הביטויים הבאים )מחלקה B יורשת ממחלקה A(? canassign<a,a>::result; canassign<b,a>::result; canassign<b*,a*>::result; כתבו ב- Java פונקציה בשם canassign2 השקולה לפונקציה מסעיף א', כלומר, פונקציה שתכשל בזמן קומפילציה אם לא ניתן הציב מופע של טיפוס T1 למשתנה מטיפוס T2. להלן הצעה לפונקציה דומה ב- Java, שבודקת האם ניתן להציב את האובייקט from למשתנה מטיפוס של.to.4 המתודה static<t1,t2> boolean canassign3(t1 from, T2 to){ Class<?> c1 = from.getclass(); Class<?> c2 = to.getclass(); return (c2.isassignablefrom(c1) ); isassignablefrom קובעת האם ניתן להציב ערך ממחלקה אחת למחלקה אחרת. כתבו קטע קוד קצר בו קריאות לcanAssign2 ו- canassign3 עם אותם ערכים יחזירו ערכים זהים )קטע הקוד צריך לעבור קומפילציה(. כתבו קטע קוד קצר בו קריאות לcanAssign2 ו- canassign3 עם אותם ערכים יחזירו ערכים שונים )קטע הקוד צריך לעבור קומפילציה(..a.b תכנות מונחה עצמים 2 227612
שאלה 0 Conformance 02( C# Generics and נק'( עבור הסעיפים הבאים נתונה המחלקה הגנרית הבאה ב-# C : 0 2 2 4 5 7 6 8 9 01 00 02 02 04 05 07 06 08 09 21 20 22 22 24 25 27 26 28 29 21 20 22 22 24 25 public class ArrayList<T> : IArrayList<T> { T[] items; // the elements array int n; // the number of elements int N; // the maximum number of elements public ArrayList(T[] arr) { items = arr; N = n = (arr == null? 0 : arr.length); public void Expand() { N = (N + 10) * 2; T[] olditems = items; items = new T[N]; for (int i=0; i < n; ++i) items[i] = olditems[i]; public void Add<U>(U u) { if (n >= N) Expand(); items[n++] = u; public T this[int i]{ // operator overloading get { if (i < 0 i >= n) throw new IndexOutOfRangeException(); return items[i]; public void Clear() { items = null; N = n = 0; items = null; public void AddEmptyCells(int n) { for(int i = 0; i < n; ++i) Add((T)null); // ToString is first defined in object. public String ToString(){ return items == null? "null" : items.tostring(); 0. קוד זה אינו מתקמפל וקיימות בו 2 שגיאות קומפילציה. מצא את שגיאות הקומפילציה. לכל שגיאת קומפילציה ציין: a. מספר שורה. b. הסיבה לשגיאה. c. מספר השורה של התיקון. d. התיקון. תכנות מונחה עצמים 3 227612
כעת נניח כי השגיאות בסעיף הקודם תוקנו. לפניכם קוד אשר משתמש בממשק.IArrayList עליכם לממש את ממשק זה כך שהקוד הבא יתקמפל ויעבוד בצורה תקינה: עבור הסעיפים הבאים נתונה המחלקה הגנרית הכתובה ב- #C: ArrayList<string> sl = new ArrayList<string>(); sl.add("a"); IArrayList<object> ol = sl; Console.WriteLine(ol[0]); class C<T> : T { private T t; public C(C<T> other) { t = other.t; הערות: הקוד הוא אינו קוד חוקי ב-# C, אך אנחנו נניח לצורך שאלה זאת כי הוא חוקי ועובד. ההגדרות בסעיפים הבאים מתייחסות רק לחוקי התאימות בכלליות ולאו דווקא לחוקי התאימות הספציפיים ב-# C. להשתמש גם ב- A. ניתן אם בכל מקום שבו ניתן להשתמש ב- B A is-a B הסעיפים: מחלקה גנרית תיחשב קו-וריאנטית ביחס לפרמטר הגנרי T אם היא מקיימת: A<X> A<Y> is-a אמ"מ. Y is-a X האם המחלקה C קו-וריאנטית לפי הגדרה זאת? אם כן הסבר, אם לא תן דוגמא קצרה שמוכיחה את הטענה שלך. מחלקה גנרית תיחשב קונטרה-וריאנטית ביחס לפרמטר הגנרי T אם היא מקיימת: A<Y> A<X> is-a אמ"מ Y. is-a X האם המחלקה C קונטרה-וריאנטית? אם כן הסבר, אם לא תן דוגמא קצרה שמוכיחה את הטענה שלך..4 תכנות מונחה עצמים 4 227612
שאלה - 3 Squeak 02( נק'( נתונה המחלקה הבאה המתארת גרף מכוון: בגרף יש צמתים vertices( ) וקשתות.)edges( קבוצת הצמתים הן,set כלומר קבוצה ללא חזרות. הקשתות מסודרות במילון,)dictionary( המפתח של המילון הוא צומת המקור והערך הוא set של צמתי היעד. ניתן להניח כי סדר המעבר על האיברים ב- set הוא לפי סדר ההכנסה. Object subclass: #Graph instancevariablenames: 'vertices edges' classvariablenames: '' pooldictionaries: '' initialize vertices := Set new. edges := Dictionary new. addvertex: u vertices add: u addedgefrom: u To: v edges at: u ifpresent: [:uedges uedges add: v] ifabsent: [ uedges uedges := Set new. uedges add: v. edges at: u put: uedges]. print: u edges at: u ifpresent: [:uedges uedges do: [:v self print: v ] ]. Transcript show: u; cr. א. מהו הפלט של הקוד הבא? g g := Graph new. g addvertex: 'a'. g addvertex: 'b'. g addvertex: 'c'. g addvertex: 'd'. g addvertex: 'e'. g addedgefrom: 'a' To: 'b'. g addedgefrom: 'a' To: 'e'. g addedgefrom: 'b' To: 'c'. g addedgefrom: 'b' To: 'd'. g print: 'a'. תכנות מונחה עצמים 5 227612
ב. הוסיפו מתודה חדשה בשם,do: המאפשרת למשתמש להגדיר פעולה שתתבצע עבור כל צומת בתת גרף של צומת נתון v, יש לעבור על כל צומת פעם אחת בדיוק..DFS על המתודה לעבור על הגרף בסדר a. b. ניתן להניח כי בגרף אין מעגלים. c. ניתן להוסיף מתודות עזר. בחברת SqueakRUs התלהבו מהמימוש הפשוט של הגרף והחליטו להוסיף ירושה מרובה ל- Squeak. המימוש יסתמך על המימוש הנתון של גרף. כלומר, חיפוש המתודות יתבסס על חיפוש בגרף. בסעיפים הבאים נעזור לחברת SqueakRUs למממש את הירושה המרובה. ג. תזכורת: המתודה canunderstand מוגדרת במחלקה :class canunderstand: t1 (self includesselector: t1) iftrue: [^ true]. superclass ifnil: [^ false]. ^ superclass canunderstand: t1 כתבו מימוש מחדש למתודה הנ"ל בעזרת המחלקה.Graph תחילה חשבו איך ניתן לייצג את גרף הירושה בצורה שתקל על מימוש הפונקציה הנ"ל ציינו את ההנחות שלכם המפורש. ד. אחת הבעיות שנוצרות כאשר תומכים בירושה מרובה היא דו-משמעות. בסעיף זה תממשו את המתודה: hasambiguity: t1 אשר מקבלת מתודה ובודקת אם קיימת דו-משמעות מיקרית בהגדרה שלה. רמז: קיימת דו-משמעות מיקרית אם קיימות שתי הגדרות של המתודה בשתי מחלקות שונות כך שלא קיים מסלול ירושה ביניהן. כתבו את הנחותיכם במפורש. תכנות מונחה עצמים 6 227612
שאלה - 4 פולימורפיזם ב- ++C )02 נק'( נתון הקוד החוקי הבא: struct A1 { int i; virtual void f() { ; struct A2 { int j; virtual void g() { ; struct B : A1, A2 {; bool aresameobject(a1* a1, A2* a2) { // your code here void test() { A1 a1; B b1, b2; A2 a2; assert( aresameobject(&a1, &b1) == false ); assert( aresameobject(&b1, &b2) == false ); assert( aresameobject(&b2, &a2) == false ); // comparing object to itself returns true assert( aresameobject(&b1, &b1) ); // comparing two NULLs returns true as well assert( aresameobject(null, NULL) ); הסבר בקצרה את ההבדלים בין static_cast ל- dynamic_cast באיזה מידע כל המרה משתמשת, מתי מובטח שהיא תעבוד ובאיזה מצבים יש להשתמש באחת ולא בשנייה. ממש את הפונקציה aresameobject כך שתחזיר true אם גם a1 וגם a2 מצביעים על אותו אובייקט B )ראה assert רביעי למעלה( או ששניהם מצביעים על NULL )ראה assert חמישי למעלה(. הנח כי הקוד הקורא לא עושה תעלולים כמו שימוש ב- reinterpret_cast כדי "לזייף" טיפוסים, על ידי הסטת מצביע למקום לא נכון באובייקט. עם זאת, הקלט עשוי להיות.NULL האם הקוד שכתבת בסעיף הקודם יעבוד גם אם הירושה של B מ- A1 ו- A2 תהיה וירטואלית? הסבר..0 הנח עכשיו שהקוד בתחילת השאלה שונה, כך שהפונקציה f איננה וירטואליות )גם הירושה איננה וירטואלית, כמו בהתחלה(. תאר )במילים או בקוד( כיצד יש לשנות את המימוש של aresameobject כך שתעבוד במצב החדש, או הסבר מדוע אי אפשר לבצע שינוי כזה..4 הנח עכשיו שהקוד בתחילת השאלה שונה, כך ששתי הפונקציות f ו- g אינן וירטואליות )גם הירושה איננה וירטואלית, כמו בהתחלה(. תאר )במילים או בקוד( כיצד יש לשנות את המימוש של aresameobject כך שתעבוד במצב החדש, או הסבר מדוע אי אפשר לבצע שינוי כזה..5 תכנות מונחה עצמים 7 227612
שאלה - 5 Classes Nested ב- Java 02( נק'( ב- Squeak, בלוק הוא למעשה מופע של המחלקה.BlockClosure רוצים לממש מנגנון דומה ב- Java. לשם כך, מוצע הממשק הבא: interface Block<T> { public T invoke(t arg); נתונה מתודה לשימוש כללי, אשר מריצה בלוק על כל אחד מאיברי מערך מספרים נתון )הפעולה שתתבצע על איברי המערך תלויה במימוש של הבלוק(: public static int foreach(int[] nums, Block<Integer> action) { int result = 0; for (int num : nums) result = action.invoke(num); return result; // this is the last action's result השלם את המתודה הבאה, שמשתמשת ב- foreach כדי לקבל את הסכום של איברי המערך. הגדר מחלקה מקוננת מהסוג המתאים, והעבר מופע שלה אל :foreach public static int sum(int[] nums) { int result = 0; // your code here return result; שימוש במחלקה אנונימית במקום בבלוק דורש כתיבה של יותר קוד, אבל נותן יותר כוח למתכנת. איזה אפשרויות קיימות בשימוש במחלקה אנונימית, ולא בבלוקים של?Squeak )רמז: חישבו מה ניתן להגדיר במחלקה אנונימית, ולא ניתן להגדיר בבלוק( במחשבה נוספת, הוחלט לשנות את המתודה,invoke כך שלא תקבל פרמטרים:.0 public T invoke(); בהתאם, גם foreach שונתה כך שלא תקבל את המערך,nums אלא רק את מספר הפעמים שיש להריץ את ה- action : public static int foreach(int count, Block<Integer> action) { int result = 0; for (int i = 0; i < count; i++) result = action.invoke(); return result; // this is the last action's result כתוב מחדש את המתודה,sum כך שתחשב סכום עם הקוד הנתון החדש )באפשרותך לשנות את כל חלקי המתודה(. תכנות מונחה עצמים 8 227612
מתכנני השפה החליטו להוסיף תכונה חדשה: רפרנס )"alias"( למשתנה, בדומה ל-++ C. לדוגמא:.4 String s1 = null; String& s2 = s1; s1 = "hello"; System.out.println(s2); // prints "hello", and not "null" לתכונה תהיה תמיכה גם ב- JVM. איזו מגבלה בשימוש במחלקות אנונימיות ומקומיות יהיה אפשר להסיר באמצעות שימוש בתכונה החדשה? הסבר. שימוש במחלקות אנונימיות לא מאפשר מימוש של יותר ממשק אחד. הסבר למה אין תועלת רבה במתן אפשרות למימוש מספר ממשקים. כלומר, מעבר לסיבות תחביריות, מדוע לא מאפשרת השפה את הדוגמאות הבאות:.5 // Can't create anonymous class that extends String and implements Iterator. new String() implements Iterator { // Can't create anonymous class that implements two interfaces. new Iterator, Comparable<String> () { התעלם בתשובתך מממשקים "מתייגים" כמו Cloneable ו- Serializable, ומיכולות עוקפות-קומפיילר כמו.reflection אם לדעתך האפשרות הזאת דווקא נדרשת והייתה צריכה להיתמך בשפה, הסבר מדוע ותן דוגמה שבה נעשה באפשרות שימוש. תכנות מונחה עצמים 9 227612
שאלת בונוס - System 5( Typing נק'(.0 מה ההבדל בין strong typing לבין?static typing 2. האם שפת תכנות יכולה להיות strongly typed אבל לא?statically typed אם כן תנו דוגמא, אחרת נמקו מדוע לא תיתכן שפה כזאת. 2. האם שפת תכנות יכולה להיות statically typed אבל לא?strongly typed אם כן תנו דוגמא, אחרת נמקו מדוע לא תיתכן שפה כזאת. תכנות מונחה עצמים 10 227612
פיתרון רשמי שאלה 1 סעיף )5 1 נק'( הפונקציה מבצעת את יעודה - תכשל )בזמן קומפילציה( אם T1 אינו תואם לT2. אילוצים על T1 ו- T2 : לשניהם קיים copy constructor ו- destructor זמין )שכן הפרמטרים לפונקציה מתקבלים by.)value ל- T1 קיים constructor חסר פרמטרים..0 סעיף )5 0 נק'( תוצאות הביטויים: true false false <T2, T1 extends T2>boolean canassign2(t1 from, T2 to){ return true; Object o1 = new Object(); Object o2 = new Object(); assert(canassign2(o1,o2) == canassign3(o1,o2)); סעיף )5 3 נק'( סעיף )5 4 נק'( Object o3 = new Integer(3); Object o4 = new Object(); assert(canassign2(o3,o4)!= canassign3(o3,o4)); תכנות מונחה עצמים 11 227612
שאלה 0 סעיף )9 1 נק'( 0. שורה 08: לא ניתן להציב איבר מטיפוס U למערך מטיפוס T מבלי לציין את האילוצים על U. תיקון שורה :07 { U:T public void Add<U>(U u) where שורה 29: לא ניתן להציב null לתוך ValueType לכן חייבים לציין ש- T הוא מחלקה. תיקון שורה :0 class class ArrayList<T> : IArrayList<T> where T: שורה 22: מכיוון ש-() ToString מוגדרת ב- Object, חייבים לציין שהפונקציה ToString של ArrayList דורסת אותה. תיקון שורה :22 { ToString() public override String בשורה 02: T[N] items = new מתקמפל ב-# C, אך הוא מוציא שגיאת קומפילציה ב- Java. ולכן מי שטען כי צריך להוסיף new() where :T בשורה 0 קיבל מלא על כך. מי שטען כי nullable types יפתרו את שגיאה השנייה קיבלו מלא. טעויות נפוצות הרבה טענו כי throw new IndexOutOfRangeException גורם לשגיאת קומפילציה ב- #C. אך כפי שנדון בתרגולים ב-# C אין checked exceptions ולכן אין פה שגיאה. public interface IArrayList<out T> { public T this[int i] { get; סעיף )3 0 נק'( We need "out" for "IArrayList<object> ol = sl; We need "public T this[int i]" for Console.WriteLine(ol[0]); טעויות נפוצות מי שכתב מימוש לפונקציות בממשק קיבל חלקי. סעיף )4 3 נק'( לפי חוקי התאימות שלמדם בהרצאות המחלקה הנ"ל היא קו-וריאנטית. נשים לב כי המחלקה C אינה מוסיפה יכולות )משתנה t הוא פרטי ומאתחלים אותו רק בקונסטרקטור( ולכן בכל מקום שניתן להשתמש במחלקה כלשהי C<X> ניתן להשתמש ב- X. ולכן אם Y יורש מ- X )כלומר ניתן להשתמש ב- Y בכל מקום שבו משתמשים ב- X ( אזי C<Y> תואם ל-< C<X. סעיף )4 4 נק'( לפי חוקי התאימות שלמדם בהרצאות המחלקה הנ"ל היא לא קונטרה-וריאנטית. תכנות מונחה עצמים 12 227612
שאלה 3 סעיף )6 1 נק'( הפתרון הוא:.c d b e a חלקי ניתן לתשובות שנראו פחות או יותר הגיוניות אך היו שגויות. סעיף )5 0 נק'( do: ablock on: avertex edges at: u ifpresent: [:uedges uedges do: [:v self do: ablock on: v ] ]. ablock value: avertex מלא ניתן גם למעבר pre-order וגם למעבר.post-order מלא ניתן למי שרשם:.aBlock do: avertex הורדו נק' למי שרשם avertex do: ablock מפני שזה מראה חוסר הבנה בסיסי ב- Squeak. לא הורדו נק' על טעויות קטנות באלגוריתם. סעיף )5 3 נק'( קיים משתנה גלובלי בשם Graph אשר מכיל את גרף הירושה. הצמתים בגרף הם המחלקות בשפה. קיימת קשת מכוונת מהבן לאבות שלו. הנחה canunderstand: t1 ablock := [:v ( v includesselector: t1) iftrue: [^true]]. Graph do: ablock on: self. ^false. פיתרון נוסף )ללא שימוש במתודה מסעיף ב'(: canunderstand: t1 (self includesselector t1) iftrue: [^true]. Graph edges at: self ifpresent: [uedges uedges do: [:v (v canunderstand t1) iftrue: [^true]]]. ^false. לא הורדו נק' על טעויות קטנות באלגוריתם. תכנות מונחה עצמים 13 227612
סעיף )4 4 נק'( היה צריך להשתמש למצוא את כל המחלקות המממשות את t1 ולכל זוג מחלקות לבדוק האם יש בינן קשרי ירושה )אחד יורש מהשני(. אם קיים זוג של מחלקות המממשות את t1 ואין בינן קשר ירושה אזי יש דו-משמעות מקרי. inheritsfrom: aclass ablock ablock := [:v v = aclass iftrue: [^true] Graph do: ablock on: self. ^false hasambiguity: t1 implementingclasses ablock implementingclasses := Set new. ablock := [:v ( v includesselector: t1) iftrue: [implementingclasses add: v]]. Graph do: ablock on: self. implementingclasses do: [:v implementingclasses do: [:u ((u inheritsfrom: v) or: (v inheritsfrom: u)) iffalse: [^true]]] ^false. מי שרשם קוד, אך היו בו טעויות חמורות בסינטקס קיבל חלקי. מי שרשם קוד שסופר את מספר המחלקות שבהן t1 מממומשת ועונה כן אם המספר גדול מ- 0 קיבל 2-2 נק' )בכפוף למימוש הספציפי(. מי שרשם אלגוריתם או רשם כי צריך לספור את מספר המחלקות שבהן t1 ממ מו נקודה. קיבל שת מי שרשם את האלגוריתם הנכון קיבל 2 נק'. תכנות מונחה עצמים 14 227612
שאלה 4 סעיף )3 1 נקודות( התקבלו הסברים בסיסיים על ההבדלים בין ההמרות, בדגש על זמן קומפילציה וזמן ריצה. הורדו נקודות בודדות למי שציין עובדות לא נכונות, ולמי שלא התייחס כלל לשאלה מתי צריך להשתמש בכל אחת מההמרות. סעיף )5 0 נקודות( התקבלו וריאציות שונות, שהשתמשו ב- dynamic_cast כדי לבדוק ששני המשתנים מצביעים על אותו אובייקט B. פתרונות שלא השתמשו ב- dynamic_cast לא התקבלו, מכיוון שהם לא תמיד נכונים: אם אובייקט A1 ואובייקט A2 נמצאים בזיכרון באותו מבנה שקיים באובייקט B, אי אפשר להבדיל ביניהם לבין אובייקט B בלי.RTTI שימוש ב- static_cast או באריתמטיקה של מצביעים - הורדה של 4 נקודות היעדר בדיקה של NULL ב- dynamic_casts הורדה של 2 נקודות היעדר טיפול בקבלה של שני NULL הורדה של 0 נקודה בדיקה לא נכונה מבחינת התוצאה הסופית הורדה של 2 נקודות טעויות נפוצות שימוש ב- try ו- catch מסביב ל- dynamic_cast. נדרש רק כשמבצעים המרה ל- reference, ולא כשמבצעים המרה למצביע. הערה הפתרון הפשוט ביותר הוא שימוש ב-<* dynamic_cast<void, כפי שמתואר כאן. אפשרות זו לא נלמדה בקורס, וכמובן גם לא נדרשה במבחן. סעיף )4 3 נקודות( אם נעשה שימוש ב- dynamic_cast, אין צורך לבצע שינויים גם אם הירושה היא וירטואלית. באמצעות RTTI ה, cast- יודע להתמודד גם אם ירושות מהסוג הזה. תשובה שלילית לא זיכתה בנקודות, אלא אם היתה טעות נגררת )בסעיף הקודם נעשה שימוש ב- static_cast, והתשובה כללה התייחסות לבעיה שבשילוב(. תשובה חיובית ללא הסבר הורדה חלקית. סעיף )4 4 נקודות( אחד ה- dynamic_cast צריך להיות מוחלף ב- static_cast. אמנם לא מובטח שה- static_cast הזה )מ- A2 ל- B ( הוא נכון, אבל אם נשווה אותו לתוצאה של ה- dynamic_cast, הן יהיו שוות רק אם מדובר באותו אובייקט B. השארת השימוש ב- dynamic_cast הורדה של 2-4 נקודות תכנות מונחה עצמים 15 227612
שימוש ב- static_cast בשתי ההמרות הורדה של 2-4 נקודות בדיקה לא נכונה מבחינת התוצאה הסופית הורדה של 2 נקודות הסבר שטוען שאי אפשר לשנות את המימוש כך שיעבוד הורדה של 4 נקודות טעויות נפוצות שימוש ב- reinterpret_cast לא הורדו נקודות, אבל זה לא רצוי בכלל. לפי מבנה האובייקט שנלמד בקורס, תת-האובייקט של A1 אכן נמצא בתחילת B. במציאות, זה לא תמיד כך, ולכן היה עדיף להשתמש ב- static_cast. סעיף )4 5 נקודות( פתרון אי אפשר לשנות את הקוד עכשיו כך שיעבוד. אי אפשר להשתמש ב- dynamic_cast, ושימוש ב- static_cast לא יחזיר תוצאות נכונות אם הפרמטרים יהיו אובייקטים שונים, שממוקמים בזיכרון בדומה לאובייקט B. הסבר שטוען שאפשר לשנות את המימוש הורדה של 2-4 נקודות מי שהסביר למה static_cast לא יעבוד אם האובייקטים מסודרים בצורה מסוימת בזיכרון קיבל בונוס של 2 נקודות. טעויות נפוצות שימוש ב- static_cast, מתוך מחשבה שאם הוא עובר קומפילציה, הוא יעשה את הבדיקה בצורה נכונה. תכנות מונחה עצמים 16 227612
שאלה 5 public static int sums(int [] nums) { int result = 0; סעיף )5 1 נק'( Block<Integer> b = new Block<Integer> () { private int sum = 0; public Integer invoke(integer arg) { sum += arg; return sum; ; result = foreach(nums, b); return result; טעויות נפוצות בסעיף התבקשתם להשתמש בסוג הכי מתאים של.inner class מי שלא השתמש במחלקות אנונימיות איבד נקודות. מי שהשתמש ב- result בתוך local class איבד נקודות. סעיף )3 0 נק'( ניתן להוסיף מתודות, שדות ומחלקות מקוננות נוספות. בנוסף ניתן לרשת ממחלקה אחרת. public static int sums2(final int [] nums) { int result = 0; Block2<Integer> b = new Block2<Integer> () { private int count = 0; private int sum = 0; public Integer invoke() { sum += nums[count]; count++; return sum; ; result = foreach(nums.length, b); return result; סעיף )5 3 נק'( טעויות נפוצות.local class בכדי שנוכל לגשת אליו מתוך final חייב להיות nums סעיף )3 4 נק'( שינוי זה מוריד את ההגבלה כי local classes יכולות לגשת רק למשתנים לוקאליים שהם.final תכנות מונחה עצמים 17 227612
סעיף )4 5 נק'( נקודות ניתנו גם לאלו שטענו כי זה לא שימושי וגם לאלה שכן בכפוף לנימוק שניתן. לא שימושי לכל משתנה יכול להיות טיפוס סטטי יחיד, בנוסף כאשר נוצר אובייקט ממחלקה אנונימית הוא יהיה מאוכסן במשתנה. ומכיוון שלמשתנה יש טיפוס סטטי יחיד זה שימושי כי מחלקה אנונימית תממש כמה ממשקים. שימושי ניתן לממש ככה ממשקים ממישקים מורכבים כמו iterable במחלקה אחת ללא צורך בגדרה מחלקה אנונימית בתוכה שמממשת את ה- iterator עצמו. למרות שלכל משתנה יכול להיות טיפוס סטטי יחיד, ה- runtime יכול לדעת כי האובייקט מממש כמה ממשקים ובעזרת המילה השמורה instanceof והמרות של המשתנה ניתן להשתמש בעובדה כי המחלקה האנונימית מממשת כמה מימשקים. תכנות מונחה עצמים 18 227612
שאלת בונוס )5 נק'( סעיף )3 1 נק'( strong typing שפה שהיא strongly typed )בדר"כ( לא מאפשרת המרות לא מפורשות בין טיפוסים שונים. כלומר לא ניתן להציב תו לתוך מספר מבלי לבצע המרה מפורשת. static typing שפה שהיא statically typed היא שפה אשר מבצעת בדיקת טיפוסים בזמן קומפילציה ולא בזמן ריצה. סעיף )1 0 נק'(.Squeak סעיף )1 3 נק'( C++ תכנות מונחה עצמים 19 227612