5.2.5.5. Inline Virtual Function
הפגיעה האמיתית בביצועים כאשר אנחנו
משתמשים ב-dynamic binding נעשית לא בקריאה לפונקציות עצמן, אלא בעובדה שאנחנו
מפספסים בגללן הזדמנויות בהן היינו יכול להשתמש ב-virtual.
מתי נוכל להשתמש ב-inline
בשילוב עם virtual functions?
- במקרה הבא לא נוכל להשתמש ב-inline – הפונקציה איננה ידועה בזמן ההידור.
void SomeClass :: some_function_member(void)
{
some_virtual_function_member();
}
- אם אנחנו מקבלים אובייקט (ולא מצביע או משתנה התייחסות
לאובייקט), אנחנו יכולים להגדיר את הפונקציה המקבלת כ-inline:
void f(SomeClass a)
{
a.some_virtual_function_member();
}
- אם קוראים לפונקציה וירטואלית בצורה
מפורשת (כך שברור איזה פונקציה תיקרא באופן מוחלט) אזי גם ניתן להשתמש ב-inline:
void f(SomeClass& a)
{
a.SomeClass::some_virtual_function_member();
}
- בתוך ctor ניתן לעשות inline
לפונקציה וירטואלית בשפת C++. ב-C++, כאשר ה-ctor
של מחלקה נגזרת מתחיל לעבוד הוא מתחיל ב-ctor של ההורים – הבניה בעצם
מתחילה מה-ctor של מחלקת הבסיס, ולאחר מכן לפי עץ ההורשה של המחלקות
בדרך. הנקודה החשובה היא שגם כשבונים מחלקה C שנורשת מ-B
שיורשת מ-A, בתחילת הבניה המחלקה היא A. במהלך הבניה, vptr
מתעדכן כל פעם ביחס למחלקה שה-ctor שייך לה. לפיכך קריאה
לפונקציה וירטואלית בתוך ה-ctor אינה ממש dynamic-binding ולכן הקומפיילר יכול
לדעת לאיזה מתודה מתכוונים בכל קריאה.
בשפת Java ניסו לפשט את המנגנון. הגישה
ב-Java היא שראשית יוצרים את העצם כולו עם כל השדות שבו ומאתחלים אותם
להיות 0. לאחר מכן מתחילים לבנות את האובייקט מהבסיס בדומה לשפת C++ ומעדכנים
את השדות לפי הבנאים שבשרשרת ההורשה. כעת ניתן לקרוא למתודות לפי
ה-dynamic binding. הרעיון מאחורי גישה זו – הטיפוס הסופי של האובייקט
נקבע עם יצירתו.
Borland style vptr
לפי מה שאני מכיר:"חסרון בגישה זו: גם כאשר איננו משתמשים ב-dynamic binding – אנחנו משלמים במקום"
לא נכון , עבור מחלקה A שאין לה מתודות דינמיות לא יווצר כלל המצביע, ולמשל עבור מחלקה B שיורשת מA פשוט נוסיף בהתחלה את המצביע, ואחרי הבלוק של A את שאר האינפורמציה של B . וככה לא משלמים על מה שלא משתמשים ועקרונות C++ נשמרים.
מה שכן באמת הcasting קצת יותר מסובך....