למה פוינטרים זה טוב ?

חובבן

New member
אני מוכרח להודות שלא הכרתי את

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

vespa

New member
התבלבלתי לגמרי

מישהו יכול לעשות סדר בדברים ? מה נסגר בסוף ? בקיצור, אני מנסה לענות כאן על שאלה, אז הנה היא, אהיה אסיר תודה אם תוכלו לעזור לי לענות : The origins of pointers can be traced to indirect addressing modes in machine languages (here I´m referring to the idea of maintaining an index register that holds the address of the operand instead of the operand itself). The inclusion of these types in high-level languages has been very controversial. What are the arguments that speak in favor of including pointer types and what are the arguments against it? What is your tendency/experience?
 

philips

New member
אנסה להשיב...

כל מתכנת בשפה זו או אחרת חייב להתמודד עם הזיכרון.. ככל שהתוכנה מורכבת וגדולה יותר עולים בד"כ גם כמות/דרישות הזיכרון שאותו צריך המתכנת ל"תפעל" וגם מורכבות התוכנה/קושי בתחזוקתה... וכתוצאה מכך עולים כמובן גם הסיכוי לשגות וזמן הדיבוג.. ברור שככל שהמתכנת מנוסה יותר ו/או בעל שיטות עבודה טובות ומסודרות יותר, הבעייה יכולה להצטמצם. לגבי הפויינטרים עצמם, כמוש שכתבת ,כשהומצאה C , לא היו הרבה אלטרנטיבות של עבודה מול הזיכרון מלבד ידנית... וכידוע כשעובדים ידנית אם לא נזהרים קורות תקלות... ככל שהתקדמו שפות התכנות ונבדקו הגורמים לתקלות בעיות התגלה שניתן לעשות "אוטומציה" מסויימת בניהול הזיכרון ע"י שימוש בגישה עקיפה - References וכו´... ובולטת במיוחד בתחום הזה היא Java שחרטה על דגלה מיומה הראשון - את הרעיון של קוד קצר,קל ,ברור וקל לתחזוקה... על מנת ליישם את כל אלו... Java מבודדת אותך מהפויינטרים ומונעת ממך זליגות זיכרון או כל תקלה אחרת (אם תרצה לשמוע על תקלות כאלו אשמח להרחיב בהודעה אחרת). ולא רק זה - Java גם מסתדרת לבד עם הקצאות הזכרון ועם ביטולן וכו´ ע"י מנגנון שנקרא GC... מנגנון זה על אלגוריתמיו השונים וישומיו ב JVM השונים הוא נושא ששווה מספר סמינרים בפני עצמם אבל השורה התחתונה היא שבחלק גדול מאוד מן המקרים הוא נותן פיתרון מצויין לנושא הפינוי/טיפול בזכרון וזאת ב"עלות נמוכה" === המתכנת לא עושה כמעט שום דבר.. משפט Philips אומר שככל שעושים פחות,שוגים פחות ... (המשוואה הראשונה נלמדת בקורסי תכנות לעצלנים...) כמובן יקומו כאלה שיאמרו שקיימות גם שיטות לעשות אוטומטיזציה חלקית של הקצאת/מחיקת זיכרון גם בשפות אחרות כמו CPP וששם גם ניתן לשלוט על כל טוב יותר..והם בעיקרון צודקים, השאלה היחידה היא כמה אתה אוהב לשלוט... (יש פורום לאוהבי שליטה בתפוז
לסיכום... לכל שיטה יש יתרונות חסרונות , וכשם שלא הולכים לחייט ואומרים לו "תפור לי בגד" , אלא מודדים קודם, מחליטים אם מדובר בבגד ים או בחליפה וכו´ גם כאן יש לעשות את ההחלטה עפ"י דרישות התוכנית וצריכה... סטטיסטית הוכח , שלא משנה כמה מסודרים וכמה מתוחכמים וכו´ , קורות הרבה יותר תקלות כשמשתמשים בשיטות הישנות יחסית של C לעבודה עם הזכרון מול JAVA.
 

voguemaster

New member
לא טוב

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

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

voguemaster

New member
שטויות!

קודם כל אני אעיר על הנקודה הקלה יותר. אין שום יתרון משמעותי לשימוש בפוינטר ל-CHAR בתור מחרוזת על פני שימוש במחלקת String של ג´אווה (או CString של MFC וכד´..). תשמע, בקשר לשליטה וכד´, אם אתה מאמין שאתה באמת מסוגל לכתוב אפליקצית עולם אמיתי בעזרת שימוש נרחב בפוינטרים ולצאת מזה בשלום ללא קושי, אתה חי באשליות. הניסיון שלי הוא בכתיבת שרתים ב-C. בתור אפליקציה שאמורה להיות יציבה וחסרת באגים עד כמה שניתן אני חושב ששרת שכתוב ב-C בהחלט משקף בדיוק את הנקודה הזו. יש לי גם מספיק חברים תוכניתנים (גם טובים ממני) בעלי ניסיון עצום, ידע עצום (גם בוגרי ממר"ם בנוסף לכל
) וגם להם הנושא בעייתי. אין שום בעיה לכתוב טוב עם פוינטרים. אין גם בעיה משמעותית בלקבל את ה"אחריות" הזו שאתה מדבר עליה. אין שום קשר בין היותך תוכניתן טוב ו"חושב" לדיון פה. אתה תחת הרושם שאם התוכניתן "חושב" והוא טוב מספיק, לא משועבד לכלים וכד´, הוא יידע להשתמש בפוינטרים נכון ולא יהיו לו בעיות. בתור מישהו כזה אני אומר לך שבעיות תמיד יהיו. ככה זה. השליטה זה דבר מצוין, אבל המטרה היא המוצר הסופי, ולפעמים לכתוב משהו טוב טוב ב-C, לכתוב אותו נכון עם פוינטרים ולדבג אותו כמו שצריך לוקח הרבה יותר זמן ממה שאתה רוצה/צריך או שיש לך. בקיצור, גם המתכנת הכי מנוסה בעולם יעשה טעויות לגבי פוינטרים, ויש סיכוי טוב שאלו לא יהיו טריוויאליות, ואז אתה שורף המון זמן על דיבוג הקוד. ללא קשר לניסיון וידע, זה תמיד קורה. לחשוב אחרת זה חסר אחריות במקרה הטוב וטיפשי במקרה הפחות טוב. כמה חורי אבטחה נמצאים בגלל כל מיני טעויות תכנות וכד´ ?? המון. גם באפליקציות שיש להן צוות פיתוח מעולה. אם צוותים שלמים טועים, צוותי פיתוח של שרתים (תחת UNIX וחלזונות וכד´) אז תהיה בטוח שתוכניתן אחד לא מסוגל להיות קורקטי וסופר-חכם כל הזמן.. ודבר אחד לסיכום: יש לשפה בעיות, אין מה לעשות. החל מניהול זיכרון, אריתמטיקת פוינטרים גרועה, חוסר מבניות בקטעי קוד (blocks) ועוד הרבה מוזרויות אחרות שרק יכולות לגרום לבלבול. אם תרצה אני אביא רשימה מדויקת יותר.... אלי
 
תשובתי

CString מייושם ע"י פוינטר פנימי ו-RefCount. מה אם היית צריך ליישם משהו דומה ל-CString ברמת העיקרון? ראה PROXY PATTERN. "הוא יידע להשתמש בפוינטרים נכון ולא יהיו לו בעיות" - זה לא מה שהתכוונתי. מה שאמרתי, זה שיהיו לו בעיות, אבל *אם* הוא יודע לנצל את הכח שבמצביעים, במקום להיבהל כל פעם שהוא צריך להתעסק איתם, אז עם הנסיון תבוא השליטה. "השליטה זה דבר מצוין, אבל המטרה היא המוצר הסופי" - אין פה "אבל". שליטה בקוד ובטכניקות הקשורות אליו תביא למוצר סופי טוב ויציב. ועכשיו באופן כללי: 1. שפות C++, C ו-JAVA כולן מבוססות מצביעים. JAVA יותר מכולם. אז אתה אומר ש-JAVA היא שפה מסוכנת שאין להשתמש בה, כי אחרת תיווצר תקורת ניפוי באגים בכל הקשור למצביעים? או שאולי עם JAVA אתה מרגיש רגוע כי יש Garbage Collector? כי אם כל מה שמפריע לך, זה הצורך ב-Deallocation ידני, אז אתה באמת מגזים בתיאור שלך של כמה זה מסוכן, ומעמיס וקשה ולא יפה ומסובך. בתור מישהו שטוען שהוא מפתח אפליקציות SERVER, אתה יודע שיש דברים הרבה יותר מסובכים בזמן דיבוג, שצריך להתמודד איתם, כמו MULTITHREADING, סינכרון תקשורת ומה לא. נשמע לי מוזר שאתה כ"כ אנטי-משהו כ"כ זניח בהשוואה לעבודה שאתה עושה. אז שאלתי היא עדיין: למה אתה מרגיש יותר רגוע ב-JAVA? 2. פולימורפיזם. אמור לי בבקשה איך היית מיישם אותו בלי מצביעים.
 

voguemaster

New member
תשובה

ג´אווה אכן מבוססת מצביעים, אבל השימוש בהם שקוף מבחינת המתכנת. למעשה הוא עובד עם רפרנסים, ואין לו שום צורך באריתמטיקת פוינטרים ודברים אחרים. ההגנה שג´אווה מספקת לך, גם בגלל ה-Collector וגם בגלל אי שימוש ישיר במצביעים זה בדיוק מה שמונע אחוז גדול מאוד מהבאגים. בהקשר של פיתוח רק אמרתי שכשמפתחים בסביבה כזו, צריך לדבג הרבה פחות. שליטה אכן מביאה למוצב טוב ויציב, אבל לדעתי בפרק זמן ארוך יותר מזה שבפיתוח בג´אווה. בהקשר של אפליקציות SERVER, רק רציתי להעיר שעל אחת כמה וכמה העניין נהיה קריטי יותר. לא רק MULTITHREADING ותקשורת הם דברים שקשים לדיבוג. בסרברים כשאתה מפשל עם פוינטר או משהו בסיסי אחר כמו לנקות זיכרון אחריך, אתה בבעיה עוד יותר קשה. רק תראה כמה זמן לוקח לבדוק יציבות של שרתים, איזה בדיקות צריך לעשות וכמה זמן צריך להשקיע כדי לגלות נזילות זיכרון ונפילות או התנהגויות מוזרות אחרות, שלא לדבר על heap corruption שזה אחד מהבאגים השנואים עליי.. העובדה שבקוד MT ועם פעולות IO מורכבות יש לך בעית פוינטרים היא הרבה יותר קריטית מאשר באופן רגיל, ולפעמים קשה יותר למצוא אותה. לכן אני מעדיף להימנע משימוש ישיר במצביעים. נו טוב, השרת שאני מפתח כרגע נכתב ב-C ואין לי הרבה ברירה, אבל אתה יודע... כשאני משווה את הפיתוח שלו לפיתוח קוד בג´אווה, יש הבדל! יש הבדל בזמן הפיתוח ובקלות היחסית שלוקח לך לדבג קוד ולהפוך אותו ליציב.. יש הבדל בכמות הבאגים. לא יודע מה להגיד, זה פשוט כך. בקשר לפולימורפיזם, אין הרבה מה להגיד. אתה חייב פוינטרים, אבל לפחות בג´אווה נחסך ממך השימוש הישיר בהם והכל הופך לקל יותר..
 
ועדיין

לא ראיתי שום דבר שונה בין JAVA לבין שימוש במצביעים ב-C, מלבד העובדה שיש GC, שמטפל בפינוי הזיכרון. אולאאאאאאיייי הבדל נוסף, הוא העובדה שלמצביע אתה עלול להציב ערך זבל (או שאתה שוכח לאתחל אותו או משהו כזה), וב-JAVA אתה עובד *רק* עם REFS, אז הם תמיד חוקיים. באופן אישי, אני אכן רואה את הפוטנציאל לבאגים פה, אבל בשום אופן אני לא חושב שאלו באגים חמורים, כמו בעיות בלוגיקה, בעיות ב-MT וכו´. ולכן אני בחהלט חושב שזהו כלי ששווה לתכניתן להתעסק איתו, כי במקרים מסויימים, השליטה בקוד חשובה יותר מזמן הפיתוח שלו. סתם בשביל הקטע, סיפור קצר וחצי-קשור: לפני הרבה זמן, פיתחתי כחלק מצוות, מודול שפונה ל-DB. אני טענתי שצריך להשתמש ב-RECORDSETS, ואילו מישהו אחר בצוות טען שפשוט צריך לבצע קריאות ישירות ל-CDatabase;:ExecuteSql. אני אמרתי ש-RECORDSETS עובדים יותר מהר, ואילו הוא טען שלא יתכן, בשני במקרים יש בסוף קריאה לפונקציית ה-ODBC API שנראת SqlExecute. ולכן מעטפת ה-CLASS של RECORDSETS רק מעיקה על הביצועים ועל קריאות הקוד. כמובן שהוא טעה. כי אמנם RECORDSETS קוראים ל-SQLEXECUTE, אבל לא באותה הדרך. CDatabase::ExecuteSQL מבצע PREPERATION ו-BINDING למחרוזת שהועברה אליו. זה אומר שעל כל מחרוזת שאתה שולח אליו, הוא מבצע PARSING מחדש. לעומת זאת, RECORDSET, מבצע פעם אחד את ה-PARSING בזמן ה-INIT. אין קריאה ל-SQLEXECUTE עם מחרוזת, אלא עם מבנה נתונים שהוא תוצר של תהליך ה-PARSING. כתבתי תוכנית קטנה, שהראתה ש-RECORDSET עובד פי 4.17 יותר מהר באיטרציה של SELECTS מאשר קריאות ל-CDatabase::ExectueSql. מה שמוכיח, שלא תמיד השימוש בפונקציות, שלוקחות את השליטה ממך ועושות כל מיני דברים לבד, הן המתאימות יותר לדרישות שלך. במקרה הזה היתה פגיעה חמורה (417%) בביצועים.
 

voguemaster

New member
לא רק זה

אלא שגם העובדה שאתה עובד עם REFS ולא עם מצביע אמיתי הופכת את החיים לקלים יותר מעוד כמה בחינות. אין סיכוי שתבצע שחרור כפול של מצביע. אספקטים נוספים שג´אווה עוזרת בהם היא אכיפה. אם יש לך באג ואתה משתמש במצביע מחורבש, לא תמיד תשים לזה לב ב-C, לעומת ג´אווה שישר תייצר לך Exception, מה שמונע הרבה באגים של חירבוש מצביעים והצבות לא חוקיות, שחרור ושימוש במצביעים שהם או NULL או לא חוקיים בצורה אחרת... קבל ח"ח על הקטע עם RECORDSETS
 
זה מאוד תלוי

באיזו שפה מדובר. לדוגמה, ב-C, כל מה שכולם רשמו נכון: הרבה יותר קל להעביר מצביע בין פונקציות, מאשר להעביר את החומר שאליו מופנה המצביע. גם מטעמי יעילות וגם מטעמי לוגיסטיקה. זה כמו ההבדל בין העברת מזוודה מלאה כסף, לבין העברת צ´ק על אותו הסכום. *אבל*, ב-C++, השימוש במצביעים הוא הרבה יותר קריטי וחשוב לכתיבה נכונה של קוד. ב-JAVA, לדוגמה, כמעט הכל מבוסס על מצביעים. וזה בגלל שבשפות שהו Object Oriented, המצביע אינו רק כתובת ל-DATA בזיכרון, אלא עלול להיות כתובת למשהו, שגם יודע לבצע פעולות מסויימות. אם יצא לך לקרוא קצת על פולימורפיזם, אז תבין למה אני מתכוון, ולמה השימוש במצביעים כ"כ חשוב.
 
למעלה