
struct או מ-enum. רק שתי המילים האלה יוצרות טיפוסים שמתנהגים כך.class. בהמשך הקורס יופיעו גם interface ו-delegate.string הוא יוצא דופן, ויוסבר בהמשך.time לפונקציה, כל הערכים משוכפלים לעותק חדש שנמצא בפונקציה.init מאפסת את העותק בלבד, והאובייקט המקורי נשאר כמו שהיה.int: int הוא struct. כששולחים num לפונקציה, הפונקציה משנה עותק בלבד, והערך המקורי נשאר 8.time myTime = new time(); היא בעצם שתי פקודות: הצהרה על משתנה, והשמה של אובייקט חדש בזיכרון.myTime אינו האובייקט עצמו, אלא תא זיכרון שמחזיק את הכתובת שלו. לאובייקט אין שם, וגישה אליו נעשית דרך שם המשתנה.init מאפסת את האובייקט המקורי, ולכן כשחוזרים ל-Main הערכים כבר השתנו.הטקסט המלא של הדקות הראשונות בהרצאה «הבדל בין Reference לבין Value» בקורס הווידאו של PracticU – כתוביות בעברית עם זמן התחלה לכל פסקה. כך תוכלו לקרוא ולחפש את הנושאים לפני שמתחילים ללמוד.
אנחנו מגיעים לאחד הסרטונים החשובים ביותר לצורך הבנה עמוקה לגבי Object Oriented. והסרטון הזה שאנחנו הולכים לראות עכשיו מתעסק עם הזיכרון.
למרות שאנחנו כבר ראינו שכאשר אנחנו יוצרים ישות חדשה על ידי קלאס או על ידי סטראק, אז יש לנו דאטה טייפ ואנחנו יכולים עכשיו לפתוח משתנה בזיכרון ואנחנו יודעים לגמרי מה קיים בזיכרון.
אז למעשה יש עוד כמה דברים שלא ראינו לגבי הזיכרון ואנחנו חייבים לשלוט בצורה מדויקת ומלאה במה קורה בזיכרון כאשר אנחנו פותחים משתנה. והנושא של השיעור הזה זה Value Type מול Reference Type.
אנחנו הולכים להבין בדיוק מה זה Value Type ומה זה Reference Type ומה ההבדל ביניהם. קודם כל הגדרה פשוטה למרות שהיא חלקית היא נכונה. Value Type זה פשוט מאוד טיפוס נתונים שנוצר מסטראק או מאינם.
רפרנס טייפ זה פשוט מאוד טיפוס נתונים שנוצר מקלאס או מאינטרפייס או מדלגייט שאינטרפייס ודלגייט אנחנו נגיע אליהם בהמשך הקורס הזה. כרגע מבחינתנו רפרנס טייפ זה טיפוס נתונים שנוצר מקלאס.
ועכשיו אנחנו הולכים להיכנס ולראות האם יש עוד הבדלים בין רפרנס טייפ לווליו טייפ מבחינת הזיכרון, ואם יש, מה המשמעות האמיתית של ההבדלים האלה?
עכשיו, יכול להיות שאתם אומרים לעצמכם, מה באמת אכפת לי מה קורה בזיכרון? אני רוצה לבנות אדם ולכן אני יוצר אישות פרסון ואז אני בונה אובייקטים של אנשים בקוד, ומה בדיוק אכפת לי ממה קורה בזיכרון?
אז למעשה מאוד אכפת לנו מה קורה בזיכרון, כי התמונה של הזיכרון משפיעה על המון דברים במהלך העריצה, ויש לזה גם חשיבות די רצינית בנוגע לכלי השלישי של Object Oriented שזה הפולימורפיזם.
ולכן אנחנו חייבים להבין לגמרי בצורה מוחלטת מה בדיוק נפתח לנו בזיכרון כאשר אנחנו פותחים אובייקט, ואיך האובייקט הזה משפיע על ריצת התוכנית שלנו.
לצורך כך אנחנו הולכים קודם כל לעשות הדגמה של Value Type. כלומר, מה שאנחנו הולכים לעשות זה לפתוח משתנה מסוג Struct, להכניס אליו ערכים ולראות משהו מאוד מעניין בקשר לזיכרון עצמו.
לכן אני הולך לחזור כרגע לתוכנית ולבנות את אחד הסטראקטים שבנינו בעבר. והסטראקט שאני הולך לבנות עכשיו זה פשוט הסטראקט time.
בואו נוסיף לעצמנו טופס חדש, נקרא לו time והוא אמור לייצג לנו זמן. ומה שאני רוצה לבנות זה ישות שמייצגת זמן ומכילה סך הכל שעה, דקה ושנייה.
אז בואו נבנה את הסטראקט שלנו, ולהלן הסטראקט. כעת כל מה שאני רוצה לעשות זה לבנות משתנה אחד מהטיפוס של הסטראקט. כל מה שאנחנו צריכים לעשות זה לרשום time, my time שווה new time.
האם זה נכון? תשובה, לא בדיוק. אנחנו כרגע יכולים להיפטר מזה. דבר ראשון, כרגע זה מתאים לנו. דרך אגב, לא התכוונתי לבנות קלאס, התכוונתי לבנות סטראקט, פשוט בגלל שפתחתי את הקובץ של הסטראקט, כי אין לנו קובץ של סטראקט והוא כתב לי פה את המילה סטראקט.
אז כרגע יש לנו פה את סטראקט טיים ואנחנו בונים משתנה time, מיי טיים, והדבר הזה הספיק לנו בשביל לבנות אובייקט בזיכרון או משתנה בזיכרון שמכיל שלושה תאים, hour, minute ו-second.
כעת אנחנו יכולים לגשת למיי טיים נקודה להכניס איזושהי שעה מייטיים נקודה מינט להכניס איזושהי דקה ומייטיים נקודה סקנד להכניס איזושהי שנייה.
אחלה. עכשיו כל מה שאני רוצה לעשות זה להציג את הנתונים. אני רוצה להציג את שעה את הדקה ואת השנייה בפורמט של שעון רגיל.
אז בואו ניקח d2 נשים אותו גם פה וגם פה וממש לראות מה יש בתוך המשתנה. יש לנו פה את ה-hour, יש לנו פה את ה-Minute ויש לנו פה את ה-Second.
למרות שיכולנו לבנות איזושהי פונקציית פרינט ולהדפיס את זה בתוך, אני כרגע אעשה את זה כאן. אין לזה חשיבות, יכלנו גם לכתוב איזושהי פונקציית פרינט.
אוקיי, עכשיו מה שאני רוצה לעשות, קודם כל להריץ את זה, בואו נסתכל שבאמת הנתונים קיימים והכל עובד כמו שצריך. שימו לב שכבר עכשיו אנחנו יכולים לראות שההגדרה הזאת הספיקה לנו ואנחנו לא באמת היינו צריכים לעשות time my time שווה new time תכף אנחנו נראה למה לא והשעה מוצגת לנו פה אבל
בואו נעשה עוד איזשהו ניסוי מעניין בואו נפתח פה עוד פונקציה ונקרא לה init time אנחנו רוצים שהפונקציה הזאת תקבל משתנה מטיפוס time נקרא לו t אנחנו רוצים שהפונקציה הזאת תאתחל את הערכים של המשתנה שהיא קיבלה.
לדוגמה, אנחנו רוצים שהיא תכניס 0 ב-Hour, אנחנו רוצים שהיא תכניס 0 ב-Minute, ואנחנו רוצים שהיא תכניס 0 ב-Second. לא הייתי חייב לרשום 0-0, פשוט זה קצת יותר אסתטי להסתכל על שעה דקה שנייה עם שתי ספרות.
ועכשיו מה שאנחנו רוצים לעשות זה לקרוא ל-Init time לשלוח לה את my time כי היא מקבלת טיפוס time ועוד פעם להדפיס את אותם ערכים עכשיו אני אתן לכם דקה אחת לעצור את הוידאו ולחשוב מה בעצם הולך להיות מודפס בהדפסה השנייה אנחנו רואים ששלחנו משתנה מסוג time לפונקציה שאיפסה אותו ואז לקחנו
והדפסנו את אותו משתנה מה בעצם הולך להיות מודפס בהדפסה השנייה. בהדפסה הראשונה ראינו שמה שהולך להיות מודפס זה הערכים האלה.
תיקחו דקה לחשוב על זה, אוקיי, ובואו נריץ. ושימו לב שהפונקציה לא השפיעה כהוא זה על המשתנה שנשלח אליה.
והשאלה היא למה. ועכשיו אנחנו הולכים לחזור למצגת ולראות בדיוק מה קרה כאשר אנחנו פתחנו משתנה שנולד מ-struct, כלומר הוא נולד מ-value type וההתנהגות הזאת זאת התנהגות שהיא זהה לכל ה-value types.
אני מזכיר לכם, value type זה טיפוס נתונים שנוצר מ-struct או מ-enum. רק שתי המילים השמורות האלה מאפשרות ליצור טיפוסי נתונים שמתנהגים בצורה כזאת.
יש משהו יוצא דופן שהוא סטרינג אבל אנחנו נדבר עליו בהמשך. כרגע זאת ההתנהגות של value type. אז מה בעצם קרה כאן?
בואו נחזור למצגת ונראה בדיוק מה הדבר שקרה? אנחנו יצרנו struct time עם hour, minute ו-second. אנחנו פתחנו משתנה וראינו שזה בעצם התחביר שמספיק לנו לפתוח בהשתנה.
ברגע שכתבנו time my time, שימו לב מה נוצר בזיכרון. נוצר ישות מורכבת, אובייקט מורכב משלושה שדות, hour, minute ו-second, מפני שאלו הם ה-data members שקיימים בתוך ה-struct time.
עכשיו, שימו לב למשהו חשוב. כל האובייקט הזה שנוצר לי בזיכרון נקרא מייטיים, זה השם של כל האובייקט. אני לא סתם מתעכב על הדברים האלה שנראים טריוויאליים, אבל עוד מעט אם תראו שהם ממש ממש לא טריוויאליים.
הדבר הבא שעשינו זה הלכנו וניגשנו למייטיים.hour הכנסנו 10, מייטיים.Minute הכנסנו 0.7, מייטיים.Second הכנסנו 30, כלומר הכנסנו ערכים לתוך השדות, וזה גרם להכנסה של הערכים האלה לתוך התאים של האובייקט עצמו.
עד כאן אין שום דבר חדש. לאחר מכן יצרנו פונקציה שנקראת init time והיא מקבלת timeT וכל מה שעשינו זה קראנו לפונקציה ושלחנו את mytime והנה מגיע הפאנצ'ליין מה זה mytime mytime זהו כל האובייקט הזה שאנחנו רואים פה ולכן אם אנחנו שולחים את my time לפונקציה, שזה בעצם כל האובייקט, אנחנו בעצם שולחים את התוכן שלו.
ולכן התוכן שלו משוכפל לתוך עותק חדש ונפרד של אובייקט, שמחזיק בדיוק את אותם ערכים שהיו לאובייקט המקורי. אבל הנקודה היא כאן שהעותק הזה זה עותק נפרד.
השם שלו מבחינת הפונקציה זה t והוא נפרד לגמרי ולא קשור לאובייקט הקודם. רק במהלך השליחה כל הערכים שלו נשלחו לפונקציה.
למה בעצם נוצר לנו פה אובייקט חדש? כי אנחנו מתעסקים עם struct וזה value type ושימו לב מה היה ההגדרה או התחביר המקורי ליצירת משתנה time.
my time יצר לנו אובייקט שלם מטיפוס time. אז כאשר אנחנו כותבים time t נוצר לנו אובייקט שלם מטיפוס time וכאשר אנחנו שולחים לפונקציה הזאת את my time אז תוכן של האובייקט המקורי שנקרא my time משוכפל למשתנה שנמצא בתוך הפונקציה ונקרא t ומקבל עותק חדש של הערכים.
שליחת הנתונים הזאת נקראת שליחת אובייקט ביי value זה השם של זה. המידע של האובייקט משוכפל לעותק חדש ולכן זה נקרא by value כי הvalue שלו משוכפל לעותק נפרד.
מה עשינו בתוך הפונקציה? בתוך הפונקציה נכנסנו ל-our minute ו-second, הכנסנו אפסים לכולם אבל מי זה בעצם t? t זה אובייקט ששייך לפונקציה, זה האובייקט הזה שאנחנו רואים פה שהוא נפרד לגמרי מהאובייקט המקורי ולכן מי ששונה כרגע זה האובייקט t כעת אנחנו בעצם יכולים להיפרד מכל הפונקציה כי היא הסתיימה
לה אנחנו חוזרים בחזרה ואנחנו מסתכלים על האובייקט המקורי ואנחנו רואים שאף אחד לא שינה אותו הפונקציה שינתה בעצם את העותק ולכן האובייקט המקורי נשאר כמו שהוא היה בהתחלה זאת הסיבה שכאשר אנחנו הרצנו את התוכנית אנחנו ראינו שהאובייקט המקורי לא השתנה בכלל כל ההדגמה הזאת הייתה מאוד חשובה בכדי להבין איך value
type פועל. הדבר הזה זהה ל-struct ול-enum, כלומר כל ה-struct וכל ה-enum מתנהגים בצורה כזאת. זאת הסיבה שכאשר היה לנו עד היום סתם אינטג'ר פשוט int num שווה 8 ואנחנו שלחנו אותו לאיזושהי פונקציה, בואו נכתוב פה איזושהי פונקציה static void init
נאמבר שמקבלת int x כל מה שהיא עושה היא פונה אליו ומכניסה אליו איזשהו ערך לא משנה מה ואנחנו עכשיו הלכנו ושלחנו את הנאמבר ל-init number נדפיס את num לפני ואחרי ואני אתן לכם שנייה אחת לפני שאני מריץ את זה לחשוב מה הולך להיות מודפס לנו ואם הבנתם כבר הולך להיות מודפס כאן שמונה וגם כאן הולך להיות מודפס שמונה בואו נריץ את זה ונסתכל שהפונקציה קיבלה את
הערך לעותק נפרד בפונקציה. זאת הסיבה שזה בעצם העברה בי-ווליו כי הווליו מועתק עצמו וזה מתנהג בצורה כזאת בגלל שאינטג'ר הוא, ניחשתם נכון, סטראקט.
זאת אומרת שהוא ווליו טייפ. זאת הסיבה שהוא מתנהג כמו שהוא מתנהג וזאת הסיבה שהמידע עובר בי-ווליו והפונקציה לא מצליחה להשפיע על המשתנה המקורי שהיא קיבלה, באשר אנחנו שולחים לפונקציה את המשתנה בשיטה הקלאסית, פשוט השם שלו, העותק משוכפל.
כעת אנחנו הולכים להבין מה זה רפרנס טייפ. אחרי שהבנו מה זה ואליו טייפ ואיך הוא מתנהג, הולכים להבין מה זה רפרנס טייפ. כל מה שאנחנו צריכים לעשות זה לשנות את הסטראקט לקלאס.
אז אחרי שאנחנו משנים את הסטראקט לקלאס, אנחנו יודעים שעכשיו הטיפוס נתונים time הוא לא מתנהג כvalue type אלא הוא מתנהג כreference type.
אז מה זה reference type? דבר ראשון התחביר הזה לא מדויק. אנחנו ראינו שתמיד מה שאנחנו עושים כאשר אנחנו יוצרים אובייקט מסוג של קלאס זה לבצע new וזה משהו שהוא must מבחינתנו.
קודם כל בואו נעשה את זה אבל כאן אני רוצה שנייה אחת לעצור ולהראות שהפקודה הזאת בנויה משני חלקים, לא מחלק אחד. הפקודה הזאת בנויה מחלק ראשון, נא ליצור מעין משתנה מסוג time שנקרא my time.
הנה הנקודה פסיק. החלק השני שלו, my time, שווה ל-new time. ועכשיו אנחנו רואים פעם ראשונה שבעצם אותה פקודה שכל הזמן יצרנו מכילה שתי פקודות.