חידה

DadleFish

New member
אמנם אין לי את הלינק לגיבוי,

אבל אתמול קראתי שההתנהגות במקרה של delete this כזה כמו שהראיתי במקור אינה מוגדרת, ולכן אין להסתמך על ההתנהגות שלה. לא ברור אם delete this ינקה רק את האב או את שניהם.
 

ברנדל

New member
כן רק ש..

this זה לא פוינטר שקשור לבן בכלל. זה פוינטר ש"הומצא" במחלקת האב. הוא מבחינתו מ type של מחלקת האב הוא לא מכיר משהו אחר. הוא פשוט מצביע על האזור בזכרון שהתקבל בהקצאה שנעשתה אומנם עבור מחלקת הבן. אבל כאשר משתמשים בו דרך פונקציה של מחלקת האב הוא נקרא והוא משויך למחלקת והוא מסוג מחלקת האב. מחלקת האב לא מכירה פוינטרים של צאצאים. אין דבר כזה שבקריאה לפונקציה תתיחס לפוינטר שמשויך לצאצא. פוינטרים של משתני מחלקה לא נמצאים ב vtable!!. ה destructor שיופעל הוא זה שמתאים לפוינטר this של מחלקת האב בהתאם ל vtable. מובן שאם PrintAndDelte הייתה וירטואלית לא היתה שום בעיה, כי this היה נקרא ממחלקת הבן וזה היה this מ type של הבן.
 

ברנדל

New member
ובהזדמנות חגיגית זו

אני רוצה לתקן טעות נפוצה: המשפט "במקרה של מפרק וירטואלי תקרא הפונקציה של הצאצא" הוא לא נכון!! במקרה של מפרק וירטואלי יקראו כל פונקציות הפירוק עד ל base!!. אם יש רק בן ואב. אם לא נגדיר מפרק ויטואלי, תקרא רק הפונקציה של האב. אם כן נגדיר, יקראו שתיהן! קודם של הבן, אחר כך של האב. מה שכן: אם לא נגדיר: ימחק לאחר מכן זכרון בגודל של האב. אם כן נגדיר: בגודל של הבן.
 

annefan

New member
אם ככה

הבעיה היא לא שה-DTOR לא וירטואלי, אלא ש-PrintAndDelete לא וירטואלית. אם היא היתה, אז לא היה leak?
 

ברנדל

New member
אישית

דווקא כן הייתי מעדיף את השיטה של virtual dtor במקרה הזה. מצמצמים כך את הפונקציונליות שמשויכת רק לבן למינימום (פירוק בלבד). ושומרים על פונקציית default של print שעוברת בהורשה ומותאמת לשתי המחלקות (code reuse).
 

annefan

New member
אני שוב משבית שמחות!

צר לי, אבל אין לי BC בבית. אני מריץ את התוכנית בשינוי אחד (הלולאה היא 10000000 ולא 1000 סיבובים) ובשלוש אפשרויות: 1. כמו שאלדד נתן 2. PrintAndDelete וירטואלית 3. PrintAndDelete לא וירטואלית אבל בלי delete. האפשרות היחידה שבה יש נזילת זכרון כלשהי כלפי חוץ (Task Manager) היא השלישית. באפשרויות 1 ו-2, התוכנית מתחילה ב-676 KB וממשיכה כך לכל אורך הדרך. Somthing is fishy here. לגבי הבחירה, תיאורטית, אני מסכים אתך, כיוון שזו מחלקה שנורשת, וממילא יש צורך באיזו פונקציה וירטואלית (או זו או ה-DTOR) כך שגם אין בזבוז בכל מקרה. מעשית, אני לא לגמרי מבין מה קורה פה, ואני לא אוהב לפתור בעיות לפני שאני מבין אותן...
 

ברנדל

New member
מה???!!

באפשרות 1 גם יש! על איזה קומפיילר אתה מריץ? תעלה לפה את הקוד שאתה מריץ (אני משתמש ב vc6)
 

annefan

New member
ומצד שני

שיניתי את CDerived שתכיל מערך של 100 string וב-CTOR אני מאתחל את כולן. עכשיו, אם אני מגדיר DTOR וירטואלי, גם אם PrintAndDelete לא וירטואלית אני לא רואה leak. אם אין DTOR, או שהוא לא וירטואלי, יש leak. יותר מזה, אני מסתכל בדבגר על ה-vtable, והיא נראית כך:
[0] 0x00401350 CBase::printAndDelete(void) * [1] 0x00401590 CDerived::`scalar deleting destructor'(unsigned int) * [2] 0x004014a0 CDerived::print(void) *​
(זה כשכל הפונקציות המוזכרות וירטואליות). למה רק PrintAndDelete פונה לאב? האם בגלל שה-DTOR מוגדר אוטומטית גם לבן?
 

ברנדל

New member
לא הבנתי מה אתה אומר

תנסה להסביר שוב ואולי תעלה קוד בעייתי. (אגב אתה לא חייב bc כנס ל taskmanager אחרי ctrl alt del ותבדוק ב performence את השינויים בזיכרון)
 

DadleFish

New member
הממממ... אתה לא משבית שמחות

באופציה הראשונה בהחלט יש זליגה. לא יודע מה הרצת ואיך, אבל יש שם זליגה ברורה לגמרי.
 

selalerer

New member
אבל אמרת שההתנהגות לא מוגדרת ב

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

DadleFish

New member
נו ברור!

ה-LEAK הוא "בתוך STL", אבל זה כתוצאה משימוש לקוי של הקוד ב-STRING.
 

annefan

New member
אני לא בטוח בקשר לזה

valgrind יודע לזהות את מקום ההקצאות ומקום השחרור. הוא לא מזהה את ההקצאה הלא משוחררת הזו. (יותר מזה, גם כשהפכתי את את ה-DTOR לוירטואלי עדיין זוהו נזילות בתוך STL.) אחרי שחשבתי על זה, זו לא הוכחה לכלום בגלל שקריאה ל-DTOR של אבא על אוביקט מסוג בן היא התנהגות לא מוגדרת, כך שהיא יכולה גם לשחרר את כל הזכרון. השאלה האמיתית פה, לדעתי, היא מהו הטיפוס של this. עוד כמה דקות אשלח הודעה אחרת על הנושא הזה.
 

selalerer

New member
ב:

אם המחלקת אב הייתה בנויה ככה (פשוט הוספתי destructor):
class CBase { public: virtual ~CBase(){}; CBase(string &s) { m_s = s; } void PrintAndDelete() { Print(); delete this; } protected: virtual void Print() = 0; protected: string m_s; };​
אז לא הייתה זליגת זיכרון? כלומר האם כן היה נקרא הdtor של m_sAddedInformation ? (נראה לי שכן, אבל אני גם שואל
)
 

selalerer

New member
רק כי הוא מקבל אותו byref....

... למיטב הבנתי לפחות. אם הוא היה מקבל byval זה כן היה חוקי?
 
למעלה